• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/html/HTMLImageElement.h"
25 
26 #include "CSSPropertyNames.h"
27 #include "HTMLNames.h"
28 #include "RuntimeEnabledFeatures.h"
29 #include "bindings/v8/ScriptEventListener.h"
30 #include "core/dom/Attribute.h"
31 #include "core/events/ThreadLocalEventNames.h"
32 #include "core/fetch/ImageResource.h"
33 #include "core/html/HTMLAnchorElement.h"
34 #include "core/html/HTMLFormElement.h"
35 #include "core/html/parser/HTMLParserIdioms.h"
36 #include "core/html/parser/HTMLSrcsetParser.h"
37 #include "core/rendering/RenderImage.h"
38 
39 using namespace std;
40 
41 namespace WebCore {
42 
43 using namespace HTMLNames;
44 
HTMLImageElement(Document & document,HTMLFormElement * form)45 HTMLImageElement::HTMLImageElement(Document& document, HTMLFormElement* form)
46     : HTMLElement(imgTag, document)
47     , m_imageLoader(this)
48     , m_form(form)
49     , m_compositeOperator(CompositeSourceOver)
50     , m_imageDevicePixelRatio(1.0f)
51 {
52     ScriptWrappable::init(this);
53     if (form)
54         form->registerImgElement(this);
55 }
56 
create(Document & document)57 PassRefPtr<HTMLImageElement> HTMLImageElement::create(Document& document)
58 {
59     return adoptRef(new HTMLImageElement(document));
60 }
61 
create(Document & document,HTMLFormElement * form)62 PassRefPtr<HTMLImageElement> HTMLImageElement::create(Document& document, HTMLFormElement* form)
63 {
64     return adoptRef(new HTMLImageElement(document, form));
65 }
66 
~HTMLImageElement()67 HTMLImageElement::~HTMLImageElement()
68 {
69     if (m_form)
70         m_form->removeImgElement(this);
71 }
72 
createForJSConstructor(Document & document,int width,int height)73 PassRefPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document& document, int width, int height)
74 {
75     RefPtr<HTMLImageElement> image = adoptRef(new HTMLImageElement(document));
76     if (width)
77         image->setWidth(width);
78     if (height)
79         image->setHeight(height);
80     return image.release();
81 }
82 
isPresentationAttribute(const QualifiedName & name) const83 bool HTMLImageElement::isPresentationAttribute(const QualifiedName& name) const
84 {
85     if (name == widthAttr || name == heightAttr || name == borderAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == valignAttr)
86         return true;
87     return HTMLElement::isPresentationAttribute(name);
88 }
89 
collectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableStylePropertySet * style)90 void HTMLImageElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
91 {
92     if (name == widthAttr)
93         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
94     else if (name == heightAttr)
95         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
96     else if (name == borderAttr)
97         applyBorderAttributeToStyle(value, style);
98     else if (name == vspaceAttr) {
99         addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
100         addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
101     } else if (name == hspaceAttr) {
102         addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
103         addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
104     } else if (name == alignAttr)
105         applyAlignmentAttributeToStyle(value, style);
106     else if (name == valignAttr)
107         addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, value);
108     else
109         HTMLElement::collectStyleForPresentationAttribute(name, value, style);
110 }
111 
imageSourceURL() const112 const AtomicString HTMLImageElement::imageSourceURL() const
113 {
114     return m_bestFitImageURL.isNull() ? fastGetAttribute(srcAttr) : m_bestFitImageURL;
115 }
116 
formOwner() const117 HTMLFormElement* HTMLImageElement::formOwner() const
118 {
119     return findFormAncestor();
120 }
121 
parseAttribute(const QualifiedName & name,const AtomicString & value)122 void HTMLImageElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
123 {
124     if (name == altAttr) {
125         if (renderer() && renderer()->isImage())
126             toRenderImage(renderer())->updateAltText();
127     } else if (name == srcAttr || name == srcsetAttr) {
128         if (RuntimeEnabledFeatures::srcsetEnabled()) {
129             ImageCandidate candidate = bestFitSourceForImageAttributes(document().devicePixelRatio(), fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr));
130             m_bestFitImageURL = candidate.toString();
131             float candidateScaleFactor = candidate.scaleFactor();
132             if (candidateScaleFactor > 0)
133                 m_imageDevicePixelRatio = 1 / candidateScaleFactor;
134             if (renderer() && renderer()->isImage())
135                 toRenderImage(renderer())->setImageDevicePixelRatio(m_imageDevicePixelRatio);
136         }
137         m_imageLoader.updateFromElementIgnoringPreviousError();
138     }
139     else if (name == usemapAttr)
140         setIsLink(!value.isNull());
141     else if (name == onbeforeloadAttr)
142         setAttributeEventListener(EventTypeNames::beforeload, createAttributeEventListener(this, name, value));
143     else if (name == compositeAttr) {
144         // FIXME: images don't support blend modes in their compositing attribute.
145         blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
146         if (!parseCompositeAndBlendOperator(value, m_compositeOperator, blendOp))
147             m_compositeOperator = CompositeSourceOver;
148     } else
149         HTMLElement::parseAttribute(name, value);
150 }
151 
altText() const152 const AtomicString& HTMLImageElement::altText() const
153 {
154     // lets figure out the alt text.. magic stuff
155     // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
156     // also heavily discussed by Hixie on bugzilla
157     if (!getAttribute(altAttr).isNull())
158         return getAttribute(altAttr);
159     // fall back to title attribute
160     return getAttribute(titleAttr);
161 }
162 
createRenderer(RenderStyle * style)163 RenderObject* HTMLImageElement::createRenderer(RenderStyle* style)
164 {
165     if (style->hasContent())
166         return RenderObject::createObject(this, style);
167 
168     RenderImage* image = new RenderImage(this);
169     image->setImageResource(RenderImageResource::create());
170     image->setImageDevicePixelRatio(m_imageDevicePixelRatio);
171     return image;
172 }
173 
canStartSelection() const174 bool HTMLImageElement::canStartSelection() const
175 {
176     if (shadow())
177         return HTMLElement::canStartSelection();
178 
179     return false;
180 }
181 
attach(const AttachContext & context)182 void HTMLImageElement::attach(const AttachContext& context)
183 {
184     HTMLElement::attach(context);
185 
186     if (renderer() && renderer()->isImage() && !m_imageLoader.hasPendingBeforeLoadEvent()) {
187         RenderImage* renderImage = toRenderImage(renderer());
188         RenderImageResource* renderImageResource = renderImage->imageResource();
189         if (renderImageResource->hasImage())
190             return;
191 
192         // If we have no image at all because we have no src attribute, set
193         // image height and width for the alt text instead.
194         if (!m_imageLoader.image() && !renderImageResource->cachedImage())
195             renderImage->setImageSizeForAltText();
196         else
197             renderImageResource->setImageResource(m_imageLoader.image());
198 
199     }
200 }
201 
insertedInto(ContainerNode * insertionPoint)202 Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* insertionPoint)
203 {
204     // m_form can be non-null if it was set in constructor.
205     if (m_form && insertionPoint->highestAncestor() != m_form->highestAncestor()) {
206         m_form->removeImgElement(this);
207         m_form = 0;
208     }
209 
210     if (!m_form) {
211         m_form = findFormAncestor();
212         if (m_form)
213             m_form->registerImgElement(this);
214     }
215 
216     // If we have been inserted from a renderer-less document,
217     // our loader may have not fetched the image, so do it now.
218     if (insertionPoint->inDocument() && !m_imageLoader.image())
219         m_imageLoader.updateFromElement();
220 
221     return HTMLElement::insertedInto(insertionPoint);
222 }
223 
removedFrom(ContainerNode * insertionPoint)224 void HTMLImageElement::removedFrom(ContainerNode* insertionPoint)
225 {
226     if (m_form)
227         m_form->removeImgElement(this);
228     m_form = 0;
229     HTMLElement::removedFrom(insertionPoint);
230 }
231 
width(bool ignorePendingStylesheets)232 int HTMLImageElement::width(bool ignorePendingStylesheets)
233 {
234     if (!renderer()) {
235         // check the attribute first for an explicit pixel value
236         bool ok;
237         int width = getAttribute(widthAttr).toInt(&ok);
238         if (ok)
239             return width;
240 
241         // if the image is available, use its width
242         if (m_imageLoader.image())
243             return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).width();
244     }
245 
246     if (ignorePendingStylesheets)
247         document().updateLayoutIgnorePendingStylesheets();
248     else
249         document().updateLayout();
250 
251     RenderBox* box = renderBox();
252     return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedWidth(), box) : 0;
253 }
254 
height(bool ignorePendingStylesheets)255 int HTMLImageElement::height(bool ignorePendingStylesheets)
256 {
257     if (!renderer()) {
258         // check the attribute first for an explicit pixel value
259         bool ok;
260         int height = getAttribute(heightAttr).toInt(&ok);
261         if (ok)
262             return height;
263 
264         // if the image is available, use its height
265         if (m_imageLoader.image())
266             return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).height();
267     }
268 
269     if (ignorePendingStylesheets)
270         document().updateLayoutIgnorePendingStylesheets();
271     else
272         document().updateLayout();
273 
274     RenderBox* box = renderBox();
275     return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedHeight(), box) : 0;
276 }
277 
naturalWidth() const278 int HTMLImageElement::naturalWidth() const
279 {
280     if (!m_imageLoader.image())
281         return 0;
282 
283     return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).width();
284 }
285 
naturalHeight() const286 int HTMLImageElement::naturalHeight() const
287 {
288     if (!m_imageLoader.image())
289         return 0;
290 
291     return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).height();
292 }
293 
isURLAttribute(const Attribute & attribute) const294 bool HTMLImageElement::isURLAttribute(const Attribute& attribute) const
295 {
296     return attribute.name() == srcAttr
297         || attribute.name() == lowsrcAttr
298         || attribute.name() == longdescAttr
299         || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#')
300         || HTMLElement::isURLAttribute(attribute);
301 }
302 
alt() const303 const AtomicString& HTMLImageElement::alt() const
304 {
305     return getAttribute(altAttr);
306 }
307 
draggable() const308 bool HTMLImageElement::draggable() const
309 {
310     // Image elements are draggable by default.
311     return !equalIgnoringCase(getAttribute(draggableAttr), "false");
312 }
313 
setHeight(int value)314 void HTMLImageElement::setHeight(int value)
315 {
316     setIntegralAttribute(heightAttr, value);
317 }
318 
src() const319 KURL HTMLImageElement::src() const
320 {
321     return document().completeURL(getAttribute(srcAttr));
322 }
323 
setSrc(const String & value)324 void HTMLImageElement::setSrc(const String& value)
325 {
326     setAttribute(srcAttr, AtomicString(value));
327 }
328 
setWidth(int value)329 void HTMLImageElement::setWidth(int value)
330 {
331     setIntegralAttribute(widthAttr, value);
332 }
333 
x() const334 int HTMLImageElement::x() const
335 {
336     RenderObject* r = renderer();
337     if (!r)
338         return 0;
339 
340     // FIXME: This doesn't work correctly with transforms.
341     FloatPoint absPos = r->localToAbsolute();
342     return absPos.x();
343 }
344 
y() const345 int HTMLImageElement::y() const
346 {
347     RenderObject* r = renderer();
348     if (!r)
349         return 0;
350 
351     // FIXME: This doesn't work correctly with transforms.
352     FloatPoint absPos = r->localToAbsolute();
353     return absPos.y();
354 }
355 
complete() const356 bool HTMLImageElement::complete() const
357 {
358     return m_imageLoader.imageComplete();
359 }
360 
addSubresourceAttributeURLs(ListHashSet<KURL> & urls) const361 void HTMLImageElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
362 {
363     HTMLElement::addSubresourceAttributeURLs(urls);
364 
365     addSubresourceURL(urls, src());
366     // FIXME: What about when the usemap attribute begins with "#"?
367     addSubresourceURL(urls, document().completeURL(getAttribute(usemapAttr)));
368 }
369 
didMoveToNewDocument(Document & oldDocument)370 void HTMLImageElement::didMoveToNewDocument(Document& oldDocument)
371 {
372     m_imageLoader.elementDidMoveToNewDocument();
373     HTMLElement::didMoveToNewDocument(oldDocument);
374 }
375 
isServerMap() const376 bool HTMLImageElement::isServerMap() const
377 {
378     if (!fastHasAttribute(ismapAttr))
379         return false;
380 
381     const AtomicString& usemap = fastGetAttribute(usemapAttr);
382 
383     // If the usemap attribute starts with '#', it refers to a map element in the document.
384     if (usemap.string()[0] == '#')
385         return false;
386 
387     return document().completeURL(stripLeadingAndTrailingHTMLSpaces(usemap)).isEmpty();
388 }
389 
imageContents()390 Image* HTMLImageElement::imageContents()
391 {
392     if (!m_imageLoader.imageComplete())
393         return 0;
394 
395     return m_imageLoader.image()->image();
396 }
397 
isInteractiveContent() const398 bool HTMLImageElement::isInteractiveContent() const
399 {
400     return fastHasAttribute(usemapAttr);
401 }
402 
403 }
404