CTTE: RenderObject's createAnonymous() and setDocumentForAnonymous() should take...
[WebKit-https.git] / Source / WebCore / rendering / RenderImage.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2000 Dirk Mueller (mueller@kde.org)
5  *           (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
6  *           (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
7  * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
8  * Copyright (C) 2010 Google Inc. All rights reserved.
9  * Copyright (C) Research In Motion Limited 2011-2012. All rights reserved.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  *
26  */
27
28 #include "config.h"
29 #include "RenderImage.h"
30
31 #include "BitmapImage.h"
32 #include "CachedImage.h"
33 #include "Font.h"
34 #include "FontCache.h"
35 #include "Frame.h"
36 #include "FrameSelection.h"
37 #include "GraphicsContext.h"
38 #include "HTMLAreaElement.h"
39 #include "HTMLImageElement.h"
40 #include "HTMLInputElement.h"
41 #include "HTMLMapElement.h"
42 #include "HTMLNames.h"
43 #include "HitTestResult.h"
44 #include "Page.h"
45 #include "PaintInfo.h"
46 #include "RenderView.h"
47 #include "SVGImage.h"
48 #include <wtf/StackStats.h>
49
50 using namespace std;
51
52 namespace WebCore {
53
54 using namespace HTMLNames;
55
56 RenderImage::RenderImage(Element* element)
57     : RenderReplaced(element, IntSize())
58     , m_needsToSetSizeForAltText(false)
59     , m_didIncrementVisuallyNonEmptyPixelCount(false)
60     , m_isGeneratedContent(false)
61 {
62     updateAltText();
63 }
64
65 RenderImage* RenderImage::createAnonymous(Document& document)
66 {
67     RenderImage* image = new (document.renderArena()) RenderImage(0);
68     image->setDocumentForAnonymous(document);
69     return image;
70 }
71
72 RenderImage::~RenderImage()
73 {
74     ASSERT(m_imageResource);
75     m_imageResource->shutdown();
76 }
77
78 void RenderImage::setImageResource(PassOwnPtr<RenderImageResource> imageResource)
79 {
80     ASSERT(!m_imageResource);
81     m_imageResource = imageResource;
82     m_imageResource->initialize(this);
83 }
84
85 // If we'll be displaying either alt text or an image, add some padding.
86 static const unsigned short paddingWidth = 4;
87 static const unsigned short paddingHeight = 4;
88
89 // Alt text is restricted to this maximum size, in pixels.  These are
90 // signed integers because they are compared with other signed values.
91 static const float maxAltTextWidth = 1024;
92 static const int maxAltTextHeight = 256;
93
94 IntSize RenderImage::imageSizeForError(CachedImage* newImage) const
95 {
96     ASSERT_ARG(newImage, newImage);
97     ASSERT_ARG(newImage, newImage->imageForRenderer(this));
98
99     IntSize imageSize;
100     if (newImage->willPaintBrokenImage()) {
101         float deviceScaleFactor = WebCore::deviceScaleFactor(&frame());
102         pair<Image*, float> brokenImageAndImageScaleFactor = newImage->brokenImage(deviceScaleFactor);
103         imageSize = brokenImageAndImageScaleFactor.first->size();
104         imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
105     } else
106         imageSize = newImage->imageForRenderer(this)->size();
107
108     // imageSize() returns 0 for the error image. We need the true size of the
109     // error image, so we have to get it by grabbing image() directly.
110     return IntSize(paddingWidth + imageSize.width() * style()->effectiveZoom(), paddingHeight + imageSize.height() * style()->effectiveZoom());
111 }
112
113 // Sets the image height and width to fit the alt text.  Returns true if the
114 // image size changed.
115 bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
116 {
117     IntSize imageSize;
118     if (newImage && newImage->imageForRenderer(this))
119         imageSize = imageSizeForError(newImage);
120     else if (!m_altText.isEmpty() || newImage) {
121         // If we'll be displaying either text or an image, add a little padding.
122         imageSize = IntSize(paddingWidth, paddingHeight);
123     }
124
125     // we have an alt and the user meant it (its not a text we invented)
126     if (!m_altText.isEmpty()) {
127         FontCachePurgePreventer fontCachePurgePreventer;
128
129         const Font& font = style()->font();
130         IntSize paddedTextSize(paddingWidth + min(ceilf(font.width(RenderBlock::constructTextRun(this, font, m_altText, style()))), maxAltTextWidth), paddingHeight + min(font.fontMetrics().height(), maxAltTextHeight));
131         imageSize = imageSize.expandedTo(paddedTextSize);
132     }
133
134     if (imageSize == intrinsicSize())
135         return false;
136
137     setIntrinsicSize(imageSize);
138     return true;
139 }
140
141 void RenderImage::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
142 {
143     RenderReplaced::styleDidChange(diff, oldStyle);
144     if (m_needsToSetSizeForAltText) {
145         if (!m_altText.isEmpty() && setImageSizeForAltText(m_imageResource->cachedImage()))
146             imageDimensionsChanged(true /* imageSizeChanged */);
147         m_needsToSetSizeForAltText = false;
148     }
149 #if ENABLE(CSS_IMAGE_RESOLUTION)
150     if (diff == StyleDifferenceLayout
151         && (oldStyle->imageResolution() != style()->imageResolution()
152             || oldStyle->imageResolutionSnap() != style()->imageResolutionSnap()
153             || oldStyle->imageResolutionSource() != style()->imageResolutionSource()))
154         imageDimensionsChanged(true /* imageSizeChanged */);
155 #endif
156 }
157
158 void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
159 {
160     // FIXME (86669): Instead of the RenderImage determining whether its document is in the page
161     // cache, the RenderImage should remove itself as a client when its document is put into the
162     // page cache.
163     if (documentBeingDestroyed() || document().inPageCache())
164         return;
165
166     if (hasBoxDecorations() || hasMask())
167         RenderReplaced::imageChanged(newImage, rect);
168
169     if (!m_imageResource)
170         return;
171
172     if (newImage != m_imageResource->imagePtr() || !newImage)
173         return;
174     
175     if (!m_didIncrementVisuallyNonEmptyPixelCount) {
176         // At a zoom level of 1 the image is guaranteed to have an integer size.
177         view().frameView().incrementVisuallyNonEmptyPixelCount(flooredIntSize(m_imageResource->imageSize(1.0f)));
178         m_didIncrementVisuallyNonEmptyPixelCount = true;
179     }
180
181     bool imageSizeChanged = false;
182
183     // Set image dimensions, taking into account the size of the alt text.
184     if (m_imageResource->errorOccurred()) {
185         if (!m_altText.isEmpty() && document().hasPendingStyleRecalc()) {
186             ASSERT(element());
187             if (element()) {
188                 m_needsToSetSizeForAltText = true;
189                 element()->setNeedsStyleRecalc(SyntheticStyleChange);
190             }
191             return;
192         }
193         imageSizeChanged = setImageSizeForAltText(m_imageResource->cachedImage());
194     }
195
196     imageDimensionsChanged(imageSizeChanged, rect);
197 }
198
199 bool RenderImage::updateIntrinsicSizeIfNeeded(const LayoutSize& newSize, bool imageSizeChanged)
200 {
201     if (newSize == intrinsicSize() && !imageSizeChanged)
202         return false;
203     if (m_imageResource->errorOccurred())
204         return imageSizeChanged;
205     setIntrinsicSize(newSize);
206     return true;
207 }
208
209 void RenderImage::updateInnerContentRect()
210 {
211     // Propagate container size to image resource.
212     LayoutRect paintRect = replacedContentRect(intrinsicSize());
213     IntSize containerSize(paintRect.width(), paintRect.height());
214     if (!containerSize.isEmpty())
215         m_imageResource->setContainerSizeForRenderer(containerSize);
216 }
217
218 void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* rect)
219 {
220 #if ENABLE(CSS_IMAGE_RESOLUTION)
221     double scale = style()->imageResolution();
222     if (style()->imageResolutionSnap() == ImageResolutionSnapPixels)
223         scale = roundForImpreciseConversion<int>(scale);
224     if (scale <= 0)
225         scale = 1;
226     bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->intrinsicSize(style()->effectiveZoom() / scale), imageSizeChanged);
227 #else
228     bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->intrinsicSize(style()->effectiveZoom()), imageSizeChanged);
229 #endif
230
231     // In the case of generated image content using :before/:after/content, we might not be
232     // in the render tree yet. In that case, we just need to update our intrinsic size.
233     // layout() will be called after we are inserted in the tree which will take care of
234     // what we are doing here.
235     if (!containingBlock())
236         return;
237
238     bool shouldRepaint = true;
239     if (intrinsicSizeChanged) {
240         if (!preferredLogicalWidthsDirty())
241             setPreferredLogicalWidthsDirty(true);
242
243         bool hasOverrideSize = hasOverrideHeight() || hasOverrideWidth();
244         if (!hasOverrideSize && !imageSizeChanged) {
245             LogicalExtentComputedValues computedValues;
246             computeLogicalWidthInRegion(computedValues);
247             LayoutUnit newWidth = computedValues.m_extent;
248             computeLogicalHeight(height(), 0, computedValues);
249             LayoutUnit newHeight = computedValues.m_extent;
250
251             imageSizeChanged = width() != newWidth || height() != newHeight;
252         }
253
254         // FIXME: We only need to recompute the containing block's preferred size
255         // if the containing block's size depends on the image's size (i.e., the container uses shrink-to-fit sizing).
256         // There's no easy way to detect that shrink-to-fit is needed, always force a layout.
257         bool containingBlockNeedsToRecomputePreferredSize =
258             style()->logicalWidth().isPercent()
259             || style()->logicalMaxWidth().isPercent()
260             || style()->logicalMinWidth().isPercent();
261
262         if (imageSizeChanged || hasOverrideSize || containingBlockNeedsToRecomputePreferredSize) {
263             shouldRepaint = false;
264             if (!selfNeedsLayout())
265                 setNeedsLayout(true);
266         }
267
268         if (everHadLayout() && !selfNeedsLayout()) {
269             // The inner content rectangle is calculated during layout, but may need an update now
270             // (unless the box has already been scheduled for layout). In order to calculate it, we
271             // may need values from the containing block, though, so make sure that we're not too
272             // early. It may be that layout hasn't even taken place once yet.
273
274             // FIXME: we should not have to trigger another call to setContainerSizeForRenderer()
275             // from here, since it's already being done during layout.
276             updateInnerContentRect();
277         }
278     }
279
280     if (shouldRepaint) {
281         LayoutRect repaintRect;
282         if (rect) {
283             // The image changed rect is in source image coordinates (pre-zooming),
284             // so map from the bounds of the image to the contentsBox.
285             repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), m_imageResource->imageSize(1.0f)), contentBoxRect()));
286             // Guard against too-large changed rects.
287             repaintRect.intersect(contentBoxRect());
288         } else
289             repaintRect = contentBoxRect();
290         
291         repaintRectangle(repaintRect);
292
293 #if USE(ACCELERATED_COMPOSITING)
294         // Tell any potential compositing layers that the image needs updating.
295         contentChanged(ImageChanged);
296 #endif
297     }
298 }
299
300 void RenderImage::notifyFinished(CachedResource* newImage)
301 {
302     if (!m_imageResource)
303         return;
304     
305     if (documentBeingDestroyed())
306         return;
307
308     invalidateBackgroundObscurationStatus();
309
310 #if USE(ACCELERATED_COMPOSITING)
311     if (newImage == m_imageResource->cachedImage()) {
312         // tell any potential compositing layers
313         // that the image is done and they can reference it directly.
314         contentChanged(ImageChanged);
315     }
316 #else
317     UNUSED_PARAM(newImage);
318 #endif
319 }
320
321 void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
322 {
323     LayoutUnit cWidth = contentWidth();
324     LayoutUnit cHeight = contentHeight();
325     LayoutUnit leftBorder = borderLeft();
326     LayoutUnit topBorder = borderTop();
327     LayoutUnit leftPad = paddingLeft();
328     LayoutUnit topPad = paddingTop();
329
330     GraphicsContext* context = paintInfo.context;
331
332     Page* page = frame().page();
333
334     if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) {
335         if (paintInfo.phase == PaintPhaseSelection)
336             return;
337
338         if (page && paintInfo.phase == PaintPhaseForeground)
339             page->addRelevantUnpaintedObject(this, visualOverflowRect());
340
341         if (cWidth > 2 && cHeight > 2) {
342             const int borderWidth = 1;
343
344             // Draw an outline rect where the image should be.
345             context->setStrokeStyle(SolidStroke);
346             context->setStrokeColor(Color::lightGray, style()->colorSpace());
347             context->setFillColor(Color::transparent, style()->colorSpace());
348             context->drawRect(pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight)));
349
350             bool errorPictureDrawn = false;
351             LayoutSize imageOffset;
352             // When calculating the usable dimensions, exclude the pixels of
353             // the ouline rect so the error image/alt text doesn't draw on it.
354             LayoutUnit usableWidth = cWidth - 2 * borderWidth;
355             LayoutUnit usableHeight = cHeight - 2 * borderWidth;
356
357             RefPtr<Image> image = m_imageResource->image();
358
359             if (m_imageResource->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) {
360                 float deviceScaleFactor = WebCore::deviceScaleFactor(&frame());
361                 // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution.
362                 pair<Image*, float> brokenImageAndImageScaleFactor = m_imageResource->cachedImage()->brokenImage(deviceScaleFactor);
363                 image = brokenImageAndImageScaleFactor.first;
364                 IntSize imageSize = image->size();
365                 imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
366                 // Center the error image, accounting for border and padding.
367                 LayoutUnit centerX = (usableWidth - imageSize.width()) / 2;
368                 if (centerX < 0)
369                     centerX = 0;
370                 LayoutUnit centerY = (usableHeight - imageSize.height()) / 2;
371                 if (centerY < 0)
372                     centerY = 0;
373                 imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth);
374
375                 ImageOrientationDescription orientationDescription;
376 #if ENABLE(CSS_IMAGE_ORIENTATION)
377                 orientationDescription.setImageOrientationEnum(style()->imageOrientation());
378                 orientationDescription.setRespectImageOrientation(shouldRespectImageOrientation());
379 #endif
380                 context->drawImage(image.get(), style()->colorSpace(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, orientationDescription);
381                 errorPictureDrawn = true;
382             }
383
384             if (!m_altText.isEmpty()) {
385                 String text = document().displayStringModifiedByEncoding(m_altText);
386                 context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace());
387                 const Font& font = style()->font();
388                 const FontMetrics& fontMetrics = font.fontMetrics();
389                 LayoutUnit ascent = fontMetrics.ascent();
390                 LayoutPoint altTextOffset = paintOffset;
391                 altTextOffset.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + ascent + (paddingHeight / 2) - borderWidth);
392
393                 // Only draw the alt text if it'll fit within the content box,
394                 // and only if it fits above the error image.
395                 TextRun textRun = RenderBlock::constructTextRun(this, font, text, style());
396                 LayoutUnit textWidth = font.width(textRun);
397                 if (errorPictureDrawn) {
398                     if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height())
399                         context->drawText(font, textRun, altTextOffset);
400                 } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height())
401                     context->drawText(font, textRun, altTextOffset);
402             }
403         }
404     } else if (m_imageResource->hasImage() && cWidth > 0 && cHeight > 0) {
405         RefPtr<Image> img = m_imageResource->image(cWidth, cHeight);
406         if (!img || img->isNull()) {
407             if (page && paintInfo.phase == PaintPhaseForeground)
408                 page->addRelevantUnpaintedObject(this, visualOverflowRect());
409             return;
410         }
411
412 #if PLATFORM(MAC)
413         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
414             paintCustomHighlight(toPoint(paintOffset - location()), style()->highlight(), true);
415 #endif
416
417         LayoutRect contentRect = contentBoxRect();
418         contentRect.moveBy(paintOffset);
419         LayoutRect paintRect = replacedContentRect(intrinsicSize());
420         paintRect.moveBy(paintOffset);
421         bool clip = !contentRect.contains(paintRect);
422         GraphicsContextStateSaver stateSaver(*context, clip);
423         if (clip)
424             context->clip(contentRect);
425
426         paintIntoRect(context, paintRect);
427         
428         if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) {
429             // For now, count images as unpainted if they are still progressively loading. We may want 
430             // to refine this in the future to account for the portion of the image that has painted.
431             LayoutRect visibleRect = intersection(paintRect, contentRect);
432             if (cachedImage()->isLoading())
433                 page->addRelevantUnpaintedObject(this, visibleRect);
434             else
435                 page->addRelevantRepaintedObject(this, visibleRect);
436         }
437     }
438 }
439
440 void RenderImage::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
441 {
442     RenderReplaced::paint(paintInfo, paintOffset);
443     
444     if (paintInfo.phase == PaintPhaseOutline)
445         paintAreaElementFocusRing(paintInfo);
446 }
447     
448 void RenderImage::paintAreaElementFocusRing(PaintInfo& paintInfo)
449 {
450     if (document().printing() || !frame().selection().isFocusedAndActive())
451         return;
452     
453     if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
454         return;
455
456     Element* focusedElement = document().focusedElement();
457     if (!focusedElement || !isHTMLAreaElement(focusedElement))
458         return;
459
460     HTMLAreaElement* areaElement = toHTMLAreaElement(focusedElement);
461     if (areaElement->imageElement() != element())
462         return;
463
464     // Even if the theme handles focus ring drawing for entire elements, it won't do it for
465     // an area within an image, so we don't call RenderTheme::supportsFocusRing here.
466
467     Path path = areaElement->computePath(this);
468     if (path.isEmpty())
469         return;
470
471     // FIXME: Do we need additional code to clip the path to the image's bounding box?
472
473     RenderStyle* areaElementStyle = areaElement->computedStyle();
474     unsigned short outlineWidth = areaElementStyle->outlineWidth();
475     if (!outlineWidth)
476         return;
477
478     paintInfo.context->drawFocusRing(path, outlineWidth,
479         areaElementStyle->outlineOffset(),
480         areaElementStyle->visitedDependentColor(CSSPropertyOutlineColor));
481 }
482
483 void RenderImage::areaElementFocusChanged(HTMLAreaElement* element)
484 {
485     ASSERT_UNUSED(element, element->imageElement() == this->element());
486
487     // It would be more efficient to only repaint the focus ring rectangle
488     // for the passed-in area element. That would require adding functions
489     // to the area element class.
490     repaint();
491 }
492
493 void RenderImage::paintIntoRect(GraphicsContext* context, const LayoutRect& rect)
494 {
495     IntRect alignedRect = pixelSnappedIntRect(rect);
496     if (!m_imageResource->hasImage() || m_imageResource->errorOccurred() || alignedRect.width() <= 0 || alignedRect.height() <= 0)
497         return;
498
499     RefPtr<Image> img = m_imageResource->image(alignedRect.width(), alignedRect.height());
500     if (!img || img->isNull())
501         return;
502
503     HTMLImageElement* imageElt = (element() && isHTMLImageElement(element())) ? toHTMLImageElement(element()) : 0;
504     CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
505     Image* image = m_imageResource->image().get();
506     bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size());
507     ImageOrientationDescription orientationDescription;
508 #if ENABLE(CSS_IMAGE_ORIENTATION)
509     orientationDescription.setImageOrientationEnum(style()->imageOrientation());
510     orientationDescription.setRespectImageOrientation(shouldRespectImageOrientation());
511 #endif
512     context->drawImage(m_imageResource->image(alignedRect.width(), alignedRect.height()).get(), style()->colorSpace(), alignedRect, compositeOperator, orientationDescription, useLowQualityScaling);
513 }
514
515 bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox*) const
516 {
517     if (!RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(bleedAvoidance))
518         return false;
519
520     return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured();
521 }
522
523 bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
524 {
525     UNUSED_PARAM(maxDepthToTest);
526     if (!m_imageResource->hasImage() || m_imageResource->errorOccurred())
527         return false;
528     if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded())
529         return false;
530     if (!contentBoxRect().contains(localRect))
531         return false;
532     EFillBox backgroundClip = style()->backgroundClip();
533     // Background paints under borders.
534     if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground())
535         return false;
536     // Background shows in padding area.
537     if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
538         return false;
539     // Object-fit may leave parts of the content box empty.
540     ObjectFit objectFit = style()->objectFit();
541     if (objectFit != ObjectFitFill && objectFit != ObjectFitCover)
542         return false;
543     // Check for image with alpha.
544     return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this);
545 }
546
547 bool RenderImage::computeBackgroundIsKnownToBeObscured()
548 {
549     if (!hasBackground())
550         return false;
551     
552     LayoutRect paintedExtent;
553     if (!getBackgroundPaintedExtent(paintedExtent))
554         return false;
555     return foregroundIsKnownToBeOpaqueInRect(paintedExtent, 0);
556 }
557
558 LayoutUnit RenderImage::minimumReplacedHeight() const
559 {
560     return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit();
561 }
562
563 HTMLMapElement* RenderImage::imageMap() const
564 {
565     HTMLImageElement* i = element() && isHTMLImageElement(element()) ? toHTMLImageElement(element()) : 0;
566     return i ? i->treeScope()->getImageMap(i->fastGetAttribute(usemapAttr)) : 0;
567 }
568
569 bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
570 {
571     HitTestResult tempResult(result.hitTestLocation());
572     bool inside = RenderReplaced::nodeAtPoint(request, tempResult, locationInContainer, accumulatedOffset, hitTestAction);
573
574     if (tempResult.innerNode() && element()) {
575         if (HTMLMapElement* map = imageMap()) {
576             LayoutRect contentBox = contentBoxRect();
577             float scaleFactor = 1 / style()->effectiveZoom();
578             LayoutPoint mapLocation = locationInContainer.point() - toLayoutSize(accumulatedOffset) - locationOffset() - toLayoutSize(contentBox.location());
579             mapLocation.scale(scaleFactor, scaleFactor);
580
581             if (map->mapMouseEvent(mapLocation, contentBox.size(), tempResult))
582                 tempResult.setInnerNonSharedNode(element());
583         }
584     }
585
586     if (!inside && result.isRectBasedTest())
587         result.append(tempResult);
588     if (inside)
589         result = tempResult;
590     return inside;
591 }
592
593 void RenderImage::updateAltText()
594 {
595     if (!element())
596         return;
597
598     if (isHTMLInputElement(element()))
599         m_altText = toHTMLInputElement(element())->altText();
600     else if (isHTMLImageElement(element()))
601         m_altText = toHTMLImageElement(element())->altText();
602 }
603
604 void RenderImage::layout()
605 {
606     StackStats::LayoutCheckPoint layoutCheckPoint;
607     RenderReplaced::layout();
608     updateInnerContentRect();
609 }
610
611 void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
612 {
613     RenderReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize);
614
615     // Our intrinsicSize is empty if we're rendering generated images with relative width/height. Figure out the right intrinsic size to use.
616     if (intrinsicSize.isEmpty() && (m_imageResource->imageHasRelativeWidth() || m_imageResource->imageHasRelativeHeight())) {
617         RenderObject* containingBlock = isOutOfFlowPositioned() ? container() : this->containingBlock();
618         if (containingBlock->isBox()) {
619             RenderBox* box = toRenderBox(containingBlock);
620             intrinsicSize.setWidth(box->availableLogicalWidth());
621             intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding));
622         }
623     }
624     // Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image.
625     if (m_imageResource && m_imageResource->errorOccurred()) {
626         intrinsicRatio = 1;
627         return;
628     }
629 }
630
631 bool RenderImage::needsPreferredWidthsRecalculation() const
632 {
633     if (RenderReplaced::needsPreferredWidthsRecalculation())
634         return true;
635     return embeddedContentBox();
636 }
637
638 RenderBox* RenderImage::embeddedContentBox() const
639 {
640     if (!m_imageResource)
641         return 0;
642
643 #if ENABLE(SVG)
644     CachedImage* cachedImage = m_imageResource->cachedImage();
645     if (cachedImage && cachedImage->image() && cachedImage->image()->isSVGImage())
646         return static_cast<SVGImage*>(cachedImage->image())->embeddedContentBox();
647 #endif
648
649     return 0;
650 }
651
652 } // namespace WebCore