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 if (!element()->getDocument()->renderer())
77 if (element()->id() == ID_OBJECT)
78 attr = element()->getAttribute(ATTR_DATA);
80 attr = element()->getAttribute(ATTR_SRC);
82 // Treat a lack of src or empty string for src as no image at all.
83 CachedImage* newImage = 0;
85 newImage = element()->getDocument()->docLoader()->requestImage(khtml::parseURL(attr));
87 if (newImage != m_image) {
89 m_imageComplete = false;
90 CachedImage* oldImage = m_image;
95 oldImage->deref(this);
98 khtml::RenderImage *renderer = static_cast<khtml::RenderImage*>(element()->renderer());
100 renderer->resetAnimation();
104 void HTMLImageLoader::dispatchLoadEvent()
108 if (m_image->isErrorImage())
109 element()->dispatchHTMLEvent(EventImpl::ERROR_EVENT, false, false);
111 element()->dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false);
115 void HTMLImageLoader::notifyFinished(CachedObject* image)
117 m_imageComplete = true;
118 DocumentImpl* document = element()->getDocument();
120 document->dispatchImageLoadEventSoon(this);
121 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
122 if (!document->ownerElement())
123 printf("Image loaded at %d\n", element()->getDocument()->elapsedTime());
126 if (element()->renderer()) {
127 RenderImage* imageObj = static_cast<RenderImage*>(element()->renderer());
128 imageObj->setImage(m_image);
132 // -------------------------------------------------------------------------
134 HTMLImageElementImpl::HTMLImageElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
135 : HTMLElementImpl(doc), m_imageLoader(this), ismap(false), m_form(f)
138 m_form->registerImgElement(this);
141 HTMLImageElementImpl::~HTMLImageElementImpl()
144 m_form->removeImgElement(this);
147 NodeImpl::Id HTMLImageElementImpl::id() const
152 bool HTMLImageElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
165 result = eReplaced; // Shared with embeds and iframes
171 return HTMLElementImpl::mapToEntry(attr, result);
174 void HTMLImageElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
179 if (m_render) static_cast<RenderImage*>(m_render)->updateAltText();
182 m_imageLoader.updateFromElement();
185 addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
188 addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
191 // border="noborder" -> border="0"
192 if(attr->value().toInt()) {
193 addCSSLength(attr, CSS_PROP_BORDER_WIDTH, attr->value());
194 addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
195 addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
196 addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
197 addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
201 addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
202 addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
205 addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
206 addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
209 addHTMLAlignment(attr);
212 addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
215 if ( attr->value().domString()[0] == '#' )
216 usemap = attr->value();
218 QString url = getDocument()->completeURL( khtml::parseURL( attr->value() ).string() );
219 // ### we remove the part before the anchor and hope
220 // the map is on the same html page....
223 m_hasAnchor = !attr->isNull();
227 case ATTR_ONABORT: // ### add support for this
228 setHTMLEventListener(EventImpl::ABORT_EVENT,
229 getDocument()->createHTMLEventListener(attr->value().string(), this));
232 setHTMLEventListener(EventImpl::ERROR_EVENT,
233 getDocument()->createHTMLEventListener(attr->value().string(), this));
236 setHTMLEventListener(EventImpl::LOAD_EVENT,
237 getDocument()->createHTMLEventListener(attr->value().string(), this));
243 _compositeOperator = attr->value().string();
248 QString newNameAttr = attr->value().string();
249 if (attached() && getDocument()->isHTMLDocument()) {
250 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
251 document->removeNamedImageOrForm(oldNameAttr);
252 document->addNamedImageOrForm(newNameAttr);
254 oldNameAttr = newNameAttr;
259 QString newIdAttr = attr->value().string();
260 if (attached() && getDocument()->isHTMLDocument()) {
261 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
262 document->removeNamedImageOrForm(oldIdAttr);
263 document->addNamedImageOrForm(newIdAttr);
265 oldIdAttr = newIdAttr;
269 HTMLElementImpl::parseHTMLAttribute(attr);
273 DOMString HTMLImageElementImpl::altText() const
275 // lets figure out the alt text.. magic stuff
276 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
277 // also heavily discussed by Hixie on bugzilla
278 DOMString alt( getAttribute( ATTR_ALT ) );
279 // fall back to title attribute
281 alt = getAttribute( ATTR_TITLE );
283 if ( alt.isNull() ) {
284 QString p = KURL( getDocument()->completeURL( getAttribute(ATTR_SRC).string() ) ).prettyURL();
286 if ( ( pos = p.findRev( '.' ) ) > 0 )
288 alt = DOMString( KStringHandler::csqueeze( p ) );
295 RenderObject *HTMLImageElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
297 return new (arena) RenderImage(this);
300 void HTMLImageElementImpl::attach()
302 HTMLElementImpl::attach();
305 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
306 imageObj->setImage(m_imageLoader.image());
309 if (getDocument()->isHTMLDocument()) {
310 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
311 document->addNamedImageOrForm(oldIdAttr);
312 document->addNamedImageOrForm(oldNameAttr);
316 void HTMLImageElementImpl::detach()
318 if (getDocument()->isHTMLDocument()) {
319 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
320 document->removeNamedImageOrForm(oldIdAttr);
321 document->removeNamedImageOrForm(oldNameAttr);
324 HTMLElementImpl::detach();
327 long HTMLImageElementImpl::width(bool ignorePendingStylesheets) const
330 // check the attribute first for an explicit pixel value
331 DOM::DOMString attrWidth = getAttribute(ATTR_WIDTH);
333 long width = attrWidth.string().toLong(&ok);
339 DOM::DocumentImpl* docimpl = getDocument();
341 if (ignorePendingStylesheets)
342 docimpl->updateLayoutIgnorePendingStylesheets();
344 docimpl->updateLayout();
351 return m_render->contentWidth();
354 long HTMLImageElementImpl::height(bool ignorePendingStylesheets) const
357 // check the attribute first for an explicit pixel value
358 DOM::DOMString attrHeight = getAttribute(ATTR_HEIGHT);
360 long height = attrHeight.string().toLong(&ok);
366 DOM::DocumentImpl* docimpl = getDocument();
368 if (ignorePendingStylesheets)
369 docimpl->updateLayoutIgnorePendingStylesheets();
371 docimpl->updateLayout();
378 return m_render->contentHeight();
381 QImage HTMLImageElementImpl::currentImage() const
383 RenderImage *r = static_cast<RenderImage*>(renderer());
385 return r->pixmap().convertToImage();
389 bool HTMLImageElementImpl::isURLAttribute(AttributeImpl *attr) const
391 return (attr->id() == ATTR_SRC || (attr->id() == ATTR_USEMAP && attr->value().domString()[0] != '#'));
394 // -------------------------------------------------------------------------
396 HTMLMapElementImpl::HTMLMapElementImpl(DocumentPtr *doc)
397 : HTMLElementImpl(doc)
401 HTMLMapElementImpl::~HTMLMapElementImpl()
404 getDocument()->removeImageMap(this);
407 NodeImpl::Id HTMLMapElementImpl::id() const
413 HTMLMapElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
414 RenderObject::NodeInfo& info)
416 //cout << "map:mapMouseEvent " << endl;
417 //cout << x_ << " " << y_ <<" "<< width_ <<" "<< height_ << endl;
418 QPtrStack<NodeImpl> nodeStack;
420 NodeImpl *current = firstChild();
425 if(nodeStack.isEmpty()) break;
426 current = nodeStack.pop();
427 current = current->nextSibling();
430 if(current->id()==ID_AREA)
432 //cout << "area found " << endl;
433 HTMLAreaElementImpl* area=static_cast<HTMLAreaElementImpl*>(current);
434 if (area->mapMouseEvent(x_,y_,width_,height_, info))
437 NodeImpl *child = current->firstChild();
440 nodeStack.push(current);
445 current = current->nextSibling();
452 void HTMLMapElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
457 // Must call base class so that hasID bit gets set.
458 HTMLElementImpl::parseHTMLAttribute(attr);
459 if (getDocument()->htmlMode() != DocumentImpl::XHtml) break;
462 getDocument()->removeImageMap(this);
463 name = attr->value();
464 if (name.length() != 0 && name[0] == '#')
466 getDocument()->addImageMap(this);
469 HTMLElementImpl::parseHTMLAttribute(attr);
473 // -------------------------------------------------------------------------
475 HTMLAreaElementImpl::HTMLAreaElementImpl(DocumentPtr *doc)
476 : HTMLAnchorElementImpl(doc)
484 HTMLAreaElementImpl::~HTMLAreaElementImpl()
486 if (m_coords) delete [] m_coords;
489 NodeImpl::Id HTMLAreaElementImpl::id() const
494 void HTMLAreaElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
499 if ( strcasecmp( attr->value(), "default" ) == 0 )
501 else if ( strcasecmp( attr->value(), "circle" ) == 0 )
503 else if ( strcasecmp( attr->value(), "poly" ) == 0 )
505 else if ( strcasecmp( attr->value(), "rect" ) == 0 )
509 if (m_coords) delete [] m_coords;
510 m_coords = attr->value().toLengthArray(m_coordsLen);
513 m_hasTarget = !attr->isNull();
520 HTMLAnchorElementImpl::parseHTMLAttribute(attr);
524 bool HTMLAreaElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
525 RenderObject::NodeInfo& info)
528 if (width_ != lastw || height_ != lasth)
530 region=getRegion(width_, height_);
531 lastw=width_; lasth=height_;
533 if (region.contains(QPoint(x_,y_)))
536 info.setInnerNode(this);
537 info.setURLElement(this);
543 QRect HTMLAreaElementImpl::getRect(RenderObject* obj) const
546 obj->absolutePosition(dx, dy);
547 QRegion region = getRegion(lastw,lasth);
548 region.translate(dx, dy);
549 return region.boundingRect();
552 QRegion HTMLAreaElementImpl::getRegion(int width_, int height_) const
558 // added broken HTML support (Dirk): some pages omit the SHAPE
559 // attribute, so we try to guess by looking at the coords count
560 // what the HTML author tried to tell us.
562 // a Poly needs at least 3 points (6 coords), so this is correct
563 if ((shape==Poly || shape==Unknown) && m_coordsLen > 5) {
564 // make sure its even
565 int len = m_coordsLen >> 1;
566 QPointArray points(len);
567 for (int i = 0; i < len; ++i)
568 points.setPoint(i, m_coords[(i<<1)].minWidth(width_),
569 m_coords[(i<<1)+1].minWidth(height_));
570 region = QRegion(points);
572 else if (shape==Circle && m_coordsLen>=3 || shape==Unknown && m_coordsLen == 3) {
573 int r = kMin(m_coords[2].minWidth(width_), m_coords[2].minWidth(height_));
574 region = QRegion(m_coords[0].minWidth(width_)-r,
575 m_coords[1].minWidth(height_)-r, 2*r, 2*r,QRegion::Ellipse);
577 else if (shape==Rect && m_coordsLen>=4 || shape==Unknown && m_coordsLen == 4) {
578 int x0 = m_coords[0].minWidth(width_);
579 int y0 = m_coords[1].minWidth(height_);
580 int x1 = m_coords[2].minWidth(width_);
581 int y1 = m_coords[3].minWidth(height_);
582 region = QRegion(x0,y0,x1-x0,y1-y0);
584 else if (shape==Default)
585 region = QRegion(0,0,width_,height_);
587 // return null region