KHtml

html_imageimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll ([email protected])
5  * (C) 1999 Antti Koivisto ([email protected])
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 "html/html_imageimpl.h"
24 #include "html/html_formimpl.h"
25 #include "html/html_documentimpl.h"
26 
27 #include "khtmlview.h"
28 #include "khtml_part.h"
29 
30 #include <kstringhandler.h>
31 
32 #include "rendering/render_image.h"
33 #include "rendering/render_flow.h"
34 #include "css/cssstyleselector.h"
35 #include "css/cssproperties.h"
36 #include "css/cssvalues.h"
37 #include "xml/dom2_eventsimpl.h"
38 
39 #include <QCharRef>
40 #include <QPoint>
41 #include <QStack>
42 #include <QImage>
43 
44 using namespace DOM;
45 using namespace khtml;
46 
47 // -------------------------------------------------------------------------
48 
49 HTMLImageElementImpl::HTMLImageElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
50  : HTMLElementImpl(doc), ismap(false), loadEventSent(true), unsafe(false), m_image(nullptr), m_form(f)
51 {
52  if (m_form) {
53  m_form->registerImgElement(this);
54  }
55 }
56 
57 HTMLImageElementImpl::~HTMLImageElementImpl()
58 {
59  if (document()) {
60  document()->removeImage(this);
61  }
62 
63  if (m_image) {
64  m_image->deref(this);
65  }
66 
67  if (m_form) {
68  m_form->removeImgElement(this);
69  }
70 }
71 
72 NodeImpl::Id HTMLImageElementImpl::id() const
73 {
74  return ID_IMG;
75 }
76 
77 void HTMLImageElementImpl::parseAttribute(AttributeImpl *attr)
78 {
79  switch (attr->id()) {
80  case ATTR_ALT:
81  setChanged();
82  break;
83  case ATTR_SRC: {
84  setChanged();
85 
86  //Start loading the image already, to generate events
87  const DOMString imgSrcUrl = attr->value().trimSpaces();
88  if (!imgSrcUrl.isEmpty()) { //### why do we not hide or something when setting this?
89  CachedImage *newImage = document()->docLoader()->requestImage(imgSrcUrl);
90  if (newImage && newImage != m_image) {
91  CachedImage *oldImage = m_image;
92  loadEventSent = false;
93  m_image = newImage;
94  m_image->ref(this);
95  if (oldImage) {
96  oldImage->deref(this);
97  }
98  }
99 
100  if (m_image) {
101  const QUrl fullURL = QUrl(m_image->url().string());
102  if (document()->origin()->taintsCanvas(fullURL)) {
103  unsafe = true;
104  }
105  }
106  }
107  }
108  break;
109  case ATTR_WIDTH:
110  if (!attr->value().isEmpty()) {
111  addCSSLength(CSS_PROP_WIDTH, attr->value());
112  } else {
113  removeCSSProperty(CSS_PROP_WIDTH);
114  }
115  break;
116  case ATTR_HEIGHT:
117  if (!attr->value().isEmpty()) {
118  addCSSLength(CSS_PROP_HEIGHT, attr->value());
119  } else {
120  removeCSSProperty(CSS_PROP_HEIGHT);
121  }
122  break;
123  case ATTR_BORDER:
124  // border="noborder" -> border="0"
125  if (attr->value().toInt()) {
126  addCSSLength(CSS_PROP_BORDER_WIDTH, attr->value());
127  addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
128  addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
129  addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
130  addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
131  } else {
132  removeCSSProperty(CSS_PROP_BORDER_WIDTH);
133  removeCSSProperty(CSS_PROP_BORDER_TOP_STYLE);
134  removeCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE);
135  removeCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE);
136  removeCSSProperty(CSS_PROP_BORDER_LEFT_STYLE);
137  }
138  break;
139  case ATTR_VSPACE:
140  addCSSLength(CSS_PROP_MARGIN_TOP, attr->value());
141  addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value());
142  break;
143  case ATTR_HSPACE:
144  addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value());
145  addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value());
146  break;
147  case ATTR_ALIGN:
148  addHTMLAlignment(attr->value());
149  break;
150  case ATTR_VALIGN:
151  addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower());
152  break;
153  case ATTR_USEMAP:
154  if (attr->value()[0] == '#') {
155  usemap = attr->value().lower();
156  } else {
157  QString url = document()->completeURL(attr->value().trimSpaces().string());
158  // ### we remove the part before the anchor and hope
159  // the map is on the same html page....
160  usemap = url;
161  }
162  m_hasAnchor = attr->val() != nullptr;
163  break;
164  case ATTR_ISMAP:
165  ismap = true;
166  break;
167  case ATTR_ONABORT: // ### add support for this
168  setHTMLEventListener(EventImpl::ABORT_EVENT,
169  document()->createHTMLEventListener(attr->value().string(), "onabort", this));
170  break;
171  case ATTR_ONERROR:
172  setHTMLEventListener(EventImpl::ERROR_EVENT,
173  document()->createHTMLEventListener(attr->value().string(), "onerror", this));
174  break;
175  case ATTR_ONLOAD:
176  setHTMLEventListener(EventImpl::LOAD_EVENT,
177  document()->createHTMLEventListener(attr->value().string(), "onload", this));
178  break;
179  case ATTR_NOSAVE:
180  break;
181  case ATTR_NAME:
182  if (inDocument() && m_name != attr->value()) {
183  document()->underDocNamedCache().remove(m_name, this);
184  document()->underDocNamedCache().add(attr->value(), this);
185  }
186  m_name = attr->value();
187  //fallthrough
188  default:
189  HTMLElementImpl::parseAttribute(attr);
190  }
191 }
192 
193 void HTMLImageElementImpl::notifyFinished(CachedObject *finishedObj)
194 {
195  if (m_image == finishedObj) {
196  document()->dispatchImageLoadEventSoon(this);
197  }
198 }
199 
200 void HTMLImageElementImpl::dispatchLoadEvent()
201 {
202  if (!loadEventSent) {
203  loadEventSent = true;
204  if (m_image->isErrorImage()) {
205  dispatchHTMLEvent(EventImpl::ERROR_EVENT, false, false);
206  } else {
207  dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false);
208  }
209  }
210 }
211 
212 DOMString HTMLImageElementImpl::altText() const
213 {
214  // lets figure out the alt text.. magic stuff
215  // https://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
216  // also heavily discussed by Hixie on bugzilla
217  DOMString alt(getAttribute(ATTR_ALT));
218  // fall back to title attribute
219  if (alt.isNull()) {
220  alt = getAttribute(ATTR_TITLE);
221  }
222 #if 0
223  if (alt.isNull()) {
224  QString p = QUrl(document()->completeURL(getAttribute(ATTR_SRC).string())).toDisplayString();
225  int pos;
226  if ((pos = p.lastIndexOf('.')) > 0) {
227  p.truncate(pos);
228  }
230  }
231 #endif
232 
233  return alt;
234 }
235 
236 void HTMLImageElementImpl::attach()
237 {
238  assert(!attached());
239  assert(!m_render);
240  assert(parentNode());
241 
242  RenderStyle *_style = document()->styleSelector()->styleForElement(this);
243  _style->ref();
244  if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
245  _style->display() != NONE) {
246  m_render = new(document()->renderArena()) RenderImage(this);
247  m_render->setStyle(_style);
248  parentNode()->renderer()->addChild(m_render, nextRenderer());
249  }
250  _style->deref();
251 
252  NodeBaseImpl::attach();
253  if (m_render) {
254  m_render->updateFromElement();
255  }
256 }
257 
258 void HTMLImageElementImpl::removedFromDocument()
259 {
260  document()->underDocNamedCache().remove(m_name, this);
261  HTMLElementImpl::removedFromDocument();
262 }
263 
264 void HTMLImageElementImpl::insertedIntoDocument()
265 {
266  document()->underDocNamedCache().add(m_name, this);
267  HTMLElementImpl::insertedIntoDocument();
268 }
269 
270 void HTMLImageElementImpl::removeId(const DOMString &id)
271 {
272  document()->underDocNamedCache().remove(id, this);
273  HTMLElementImpl::removeId(id);
274 }
275 
276 void HTMLImageElementImpl::addId(const DOMString &id)
277 {
278  document()->underDocNamedCache().add(id, this);
279  HTMLElementImpl::addId(id);
280 }
281 
282 long HTMLImageElementImpl::width() const
283 {
284  if (!m_render) {
285  DOMString widthAttr = getAttribute(ATTR_WIDTH);
286  if (!widthAttr.isNull()) {
287  return widthAttr.toInt();
288  } else if (m_image && m_image->pixmap_size().isValid()) {
289  return m_image->pixmap_size().width();
290  } else {
291  return 0;
292  }
293  }
294 
295  document()->updateLayout();
296 
297  return m_render ? m_render->contentWidth() :
298  getAttribute(ATTR_WIDTH).toInt();
299 }
300 
301 long HTMLImageElementImpl::height() const
302 {
303  if (!m_render) {
304  DOMString heightAttr = getAttribute(ATTR_HEIGHT);
305  if (!heightAttr.isNull()) {
306  return heightAttr.toInt();
307  } else if (m_image && m_image->pixmap_size().isValid()) {
308  return m_image->pixmap_size().height();
309  } else {
310  return 0;
311  }
312  }
313 
314  document()->updateLayout();
315 
316  return m_render ? m_render->contentHeight() :
317  getAttribute(ATTR_HEIGHT).toInt();
318 }
319 
320 void HTMLImageElementImpl::setWidth(long width)
321 {
322  setAttribute(ATTR_WIDTH, QString::number(width));
323 }
324 
325 void HTMLImageElementImpl::setHeight(long height)
326 {
327  setAttribute(ATTR_HEIGHT, QString::number(height));
328 }
329 
330 QImage HTMLImageElementImpl::currentImage() const
331 {
332  if (!complete() || !m_image || !m_image->image()) {
333  return QImage();
334  }
335 
336  QImage *im = m_image->image()->qimage();
337  if (im) {
338  return *im;
339  } else {
340  return QImage();
341  }
342 }
343 
344 long HTMLImageElementImpl::x() const
345 {
346  if (renderer()) {
347  int x = 0;
348  int y = 0;
349  renderer()->absolutePosition(x, y);
350  return x;
351  }
352  return 0;
353 }
354 
355 long HTMLImageElementImpl::y() const
356 {
357  if (renderer()) {
358  int x = 0;
359  int y = 0;
360  renderer()->absolutePosition(x, y);
361  return y;
362  }
363  return 0;
364 }
365 
366 QPixmap HTMLImageElementImpl::currentPixmap() const
367 {
368  if (m_image) {
369  return m_image->pixmap();
370  }
371 
372  return QPixmap();
373 }
374 
375 bool HTMLImageElementImpl::complete() const
376 {
377  return m_image && m_image->isComplete();
378 }
379 
380 // -------------------------------------------------------------------------
381 
382 HTMLMapElementImpl::HTMLMapElementImpl(DocumentImpl *doc)
383  : HTMLElementImpl(doc)
384 {
385 }
386 
387 HTMLMapElementImpl::~HTMLMapElementImpl()
388 {
389  if (document() && document()->isHTMLDocument()) {
390  static_cast<HTMLDocumentImpl *>(document())->mapMap.remove(name);
391  }
392 }
393 
394 NodeImpl::Id HTMLMapElementImpl::id() const
395 {
396  return ID_MAP;
397 }
398 
399 bool
400 HTMLMapElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
401  RenderObject::NodeInfo &info)
402 {
403  //cout << "map:mapMouseEvent " << endl;
404  //cout << x_ << " " << y_ <<" "<< width_ <<" "<< height_ << endl;
405  QStack<NodeImpl *> nodeStack;
406 
407  NodeImpl *current = firstChild();
408  while (1) {
409  if (!current) {
410  if (nodeStack.isEmpty()) {
411  break;
412  }
413  current = nodeStack.pop();
414  current = current->nextSibling();
415  continue;
416  }
417  if (current->id() == ID_AREA) {
418  //cout << "area found " << endl;
419  HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(current);
420  if (area->mapMouseEvent(x_, y_, width_, height_, info)) {
421  return true;
422  }
423  }
424  NodeImpl *child = current->firstChild();
425  if (child) {
426  nodeStack.push(current);
427  current = child;
428  } else {
429  current = current->nextSibling();
430  }
431  }
432 
433  return false;
434 }
435 
436 void HTMLMapElementImpl::parseAttribute(AttributeImpl *attr)
437 {
438  switch (attr->id()) {
439  case ATTR_ID:
440  if (document()->htmlMode() != DocumentImpl::XHtml) {
441  HTMLElementImpl::parseAttribute(attr);
442  break;
443  } else {
444  // add name with full url:
445  QString url = document()->completeURL(attr->value().trimSpaces().string());
446  if (document()->isHTMLDocument()) {
447  static_cast<HTMLDocumentImpl *>(document())->mapMap[url] = this;
448  }
449  }
450  // fall through
451  case ATTR_NAME: {
452  DOMString s = attr->value();
453  if (*s.unicode() == '#') {
454  name = QString(s.unicode() + 1, s.length() - 1).toLower();
455  } else {
456  name = s.string().toLower();
457  }
458  // ### make this work for XML documents, e.g. in case of <html:map...>
459  if (document()->isHTMLDocument()) {
460  static_cast<HTMLDocumentImpl *>(document())->mapMap[name] = this;
461  }
462 
463  //fallthrough
464  }
465  default:
466  HTMLElementImpl::parseAttribute(attr);
467  }
468 }
469 
470 HTMLCollectionImpl *HTMLMapElementImpl::areas()
471 {
472  return new HTMLCollectionImpl(this, HTMLCollectionImpl::MAP_AREAS);
473 }
474 
475 // -------------------------------------------------------------------------
476 
477 HTMLAreaElementImpl::HTMLAreaElementImpl(DocumentImpl *doc)
478  : HTMLAnchorElementImpl(doc)
479 {
480  m_coords = nullptr;
481  m_coordsLen = 0;
482  nohref = false;
483  shape = Unknown;
484  lasth = lastw = -1;
485 }
486 
487 HTMLAreaElementImpl::~HTMLAreaElementImpl()
488 {
489  delete [] m_coords;
490 }
491 
492 NodeImpl::Id HTMLAreaElementImpl::id() const
493 {
494  return ID_AREA;
495 }
496 
497 void HTMLAreaElementImpl::parseAttribute(AttributeImpl *attr)
498 {
499  switch (attr->id()) {
500  case ATTR_SHAPE:
501  if (strcasecmp(attr->value(), "default") == 0) {
502  shape = Default;
503  } else if (strcasecmp(attr->value(), "circle") == 0) {
504  shape = Circle;
505  } else if (strcasecmp(attr->value(), "poly") == 0 || strcasecmp(attr->value(), "polygon") == 0) {
506  shape = Poly;
507  } else if (strcasecmp(attr->value(), "rect") == 0) {
508  shape = Rect;
509  }
510  break;
511  case ATTR_COORDS:
512  delete [] m_coords;
513  m_coords = attr->val()->toCoordsArray(m_coordsLen);
514  break;
515  case ATTR_NOHREF:
516  nohref = attr->val() != nullptr;
517  break;
518  case ATTR_TARGET:
519  m_hasTarget = attr->val() != nullptr;
520  break;
521  case ATTR_ALT:
522  break;
523  case ATTR_ACCESSKEY:
524  break;
525  default:
526  HTMLAnchorElementImpl::parseAttribute(attr);
527  }
528 }
529 
530 bool HTMLAreaElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
531  RenderObject::NodeInfo &info)
532 {
533  bool inside = false;
534  if (width_ != lastw || height_ != lasth) {
535  region = getRegion(width_, height_);
536  lastw = width_; lasth = height_;
537  }
538  if (region.contains(QPoint(x_, y_))) {
539  inside = true;
540  info.setInnerNode(this);
541  info.setURLElement(this);
542  }
543 
544  return inside;
545 }
546 
547 QRect HTMLAreaElementImpl::getRect() const
548 {
549  if (parentNode()->renderer() == nullptr) {
550  return QRect();
551  }
552  int dx, dy;
553  if (!parentNode()->renderer()->absolutePosition(dx, dy)) {
554  return QRect();
555  }
556  QRegion region = getRegion(lastw, lasth);
557  region.translate(dx, dy);
558  return region.boundingRect();
559 }
560 
561 QRegion HTMLAreaElementImpl::getRegion(int width_, int height_) const
562 {
563  QRegion region;
564  if (!m_coords) {
565  return region;
566  }
567 
568  // added broken HTML support (Dirk): some pages omit the SHAPE
569  // attribute, so we try to guess by looking at the coords count
570  // what the HTML author tried to tell us.
571 
572  // a Poly needs at least 3 points (6 coords), so this is correct
573  if ((shape == Poly || shape == Unknown) && m_coordsLen > 5) {
574  // make sure it is even
575  int len = m_coordsLen >> 1;
576  QPolygon points(len);
577  for (int i = 0; i < len; ++i)
578  points.setPoint(i, m_coords[(i << 1)].minWidth(width_),
579  m_coords[(i << 1) + 1].minWidth(height_));
580  region = QRegion(points);
581  } else if ((shape == Circle && m_coordsLen >= 3) || (shape == Unknown && m_coordsLen == 3)) {
582  int r = qMin(m_coords[2].minWidth(width_), m_coords[2].minWidth(height_));
583  region = QRegion(m_coords[0].minWidth(width_) - r,
584  m_coords[1].minWidth(height_) - r, 2 * r, 2 * r, QRegion::Ellipse);
585  } else if ((shape == Rect && m_coordsLen >= 4) || (shape == Unknown && m_coordsLen == 4)) {
586  int x0 = m_coords[0].minWidth(width_);
587  int y0 = m_coords[1].minWidth(height_);
588  int x1 = m_coords[2].minWidth(width_);
589  int y1 = m_coords[3].minWidth(height_);
590  // use qMin () and qAbs () to make sure that this works for any pair
591  // of opposite corners (x0,y0) and (x1,y1)
592  region = QRegion(qMin(x0, x1), qMin(y0, y1), qAbs(x1 - x0), qAbs(y1 - y0));
593  } else if (shape == Default) {
594  region = QRegion(0, 0, width_, height_);
595  }
596  // else
597  // return null region
598 
599  return region;
600 }
QString toDisplayString(QUrl::FormattingOptions options) const const
void truncate(int position)
void translate(int dx, int dy)
This file is part of the HTML rendering engine for KDE.
void push(const T &t)
QRect boundingRect() const const
a cached image
Definition: loader.h:359
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
void setAttribute(const DOMString &name, const DOMString &value)
Adds a new attribute.
QString number(int n, int base)
Node parentNode() const
The parent of this node.
Definition: dom_node.cpp:250
DOMString name() const
Names the map (for use with usemap ).
Definition: html_image.cpp:479
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
KCOREADDONS_EXPORT QString csqueeze(const QString &str, int maxlen=40)
QString toLower() const const
The Rect interface is used to represent any rect value.
Definition: css_value.h:639
Node firstChild() const
The first child of this node.
Definition: dom_node.cpp:266
This library provides a full-featured HTML parser and widget.
bool isEmpty() const const
DOMString getAttribute(const DOMString &name)
Retrieves an attribute value by name.
DOMString trimSpaces() const
Returns a string with Space Characters removed from the start and the end.
Definition: dom_string.cpp:345
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:02 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.