accddc661dacf15cb8113d74069245dcd498c1da
[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(node());
187             if (node()) {
188                 m_needsToSetSizeForAltText = true;
189                 node()->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::imageDimensionsChanged(bool imageSizeChanged, const IntRect* rect)
210 {
211 #if ENABLE(CSS_IMAGE_RESOLUTION)
212     double scale = style()->imageResolution();
213     if (style()->imageResolutionSnap() == ImageResolutionSnapPixels)
214         scale = roundForImpreciseConversion<int>(scale);
215     if (scale <= 0)
216         scale = 1;
217     bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->imageSize(style()->effectiveZoom() / scale), imageSizeChanged);
218 #else
219     bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->imageSize(style()->effectiveZoom()), imageSizeChanged);
220 #endif
221
222     // In the case of generated image content using :before/:after/content, we might not be
223     // in the render tree yet. In that case, we just need to update our intrinsic size.
224     // layout() will be called after we are inserted in the tree which will take care of
225     // what we are doing here.
226     if (!containingBlock())
227         return;
228
229     bool shouldRepaint = true;
230     if (intrinsicSizeChanged) {
231         if (!preferredLogicalWidthsDirty())
232             setPreferredLogicalWidthsDirty(true);
233
234         bool hasOverrideSize = hasOverrideHeight() || hasOverrideWidth();
235         if (!hasOverrideSize && !imageSizeChanged) {
236             LogicalExtentComputedValues computedValues;
237             computeLogicalWidthInRegion(computedValues);
238             LayoutUnit newWidth = computedValues.m_extent;
239             computeLogicalHeight(height(), 0, computedValues);
240             LayoutUnit newHeight = computedValues.m_extent;
241
242             imageSizeChanged = width() != newWidth || height() != newHeight;
243         }
244
245         // FIXME: We only need to recompute the containing block's preferred size
246         // if the containing block's size depends on the image's size (i.e., the container uses shrink-to-fit sizing).
247         // There's no easy way to detect that shrink-to-fit is needed, always force a layout.
248         bool containingBlockNeedsToRecomputePreferredSize =
249             style()->logicalWidth().isPercent()
250             || style()->logicalMaxWidth().isPercent()
251             || style()->logicalMinWidth().isPercent();
252
253         if (imageSizeChanged || hasOverrideSize || containingBlockNeedsToRecomputePreferredSize) {
254             shouldRepaint = false;
255             if (!selfNeedsLayout())
256                 setNeedsLayout(true);
257         }
258     }
259
260     if (shouldRepaint) {
261         LayoutRect repaintRect;
262         if (rect) {
263             // The image changed rect is in source image coordinates (pre-zooming),
264             // so map from the bounds of the image to the contentsBox.
265             repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), m_imageResource->imageSize(1.0f)), contentBoxRect()));
266             // Guard against too-large changed rects.
267             repaintRect.intersect(contentBoxRect());
268         } else
269             repaintRect = contentBoxRect();
270         
271         repaintRectangle(repaintRect);
272
273 #if USE(ACCELERATED_COMPOSITING)
274         // Tell any potential compositing layers that the image needs updating.
275         contentChanged(ImageChanged);
276 #endif
277     }
278 }
279
280 void RenderImage::notifyFinished(CachedResource* newImage)
281 {
282     if (!m_imageResource)
283         return;
284     
285     if (documentBeingDestroyed())
286         return;
287
288     invalidateBackgroundObscurationStatus();
289
290 #if USE(ACCELERATED_COMPOSITING)
291     if (newImage == m_imageResource->cachedImage()) {
292         // tell any potential compositing layers
293         // that the image is done and they can reference it directly.
294         contentChanged(ImageChanged);
295     }
296 #else
297     UNUSED_PARAM(newImage);
298 #endif
299 }
300
301 void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
302 {
303     LayoutUnit cWidth = contentWidth();
304     LayoutUnit cHeight = contentHeight();
305     LayoutUnit leftBorder = borderLeft();
306     LayoutUnit topBorder = borderTop();
307     LayoutUnit leftPad = paddingLeft();
308     LayoutUnit topPad = paddingTop();
309
310     GraphicsContext* context = paintInfo.context;
311
312     Page* page = frame().page();
313
314     if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) {
315         if (paintInfo.phase == PaintPhaseSelection)
316             return;
317
318         if (page && paintInfo.phase == PaintPhaseForeground)
319             page->addRelevantUnpaintedObject(this, visualOverflowRect());
320
321         if (cWidth > 2 && cHeight > 2) {
322             const int borderWidth = 1;
323
324             // Draw an outline rect where the image should be.
325             context->setStrokeStyle(SolidStroke);
326             context->setStrokeColor(Color::lightGray, style()->colorSpace());
327             context->setFillColor(Color::transparent, style()->colorSpace());
328             context->drawRect(pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight)));
329
330             bool errorPictureDrawn = false;
331             LayoutSize imageOffset;
332             // When calculating the usable dimensions, exclude the pixels of
333             // the ouline rect so the error image/alt text doesn't draw on it.
334             LayoutUnit usableWidth = cWidth - 2 * borderWidth;
335             LayoutUnit usableHeight = cHeight - 2 * borderWidth;
336
337             RefPtr<Image> image = m_imageResource->image();
338
339             if (m_imageResource->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) {
340                 float deviceScaleFactor = WebCore::deviceScaleFactor(&frame());
341                 // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution.
342                 pair<Image*, float> brokenImageAndImageScaleFactor = m_imageResource->cachedImage()->brokenImage(deviceScaleFactor);
343                 image = brokenImageAndImageScaleFactor.first;
344                 IntSize imageSize = image->size();
345                 imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
346                 // Center the error image, accounting for border and padding.
347                 LayoutUnit centerX = (usableWidth - imageSize.width()) / 2;
348                 if (centerX < 0)
349                     centerX = 0;
350                 LayoutUnit centerY = (usableHeight - imageSize.height()) / 2;
351                 if (centerY < 0)
352                     centerY = 0;
353                 imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth);
354
355                 ImageOrientationDescription orientationDescription;
356 #if ENABLE(CSS_IMAGE_ORIENTATION)
357                 orientationDescription.setImageOrientationEnum(style()->imageOrientation());
358                 orientationDescription.setRespectImageOrientation(shouldRespectImageOrientation());
359 #endif
360                 context->drawImage(image.get(), style()->colorSpace(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, orientationDescription);
361                 errorPictureDrawn = true;
362             }
363
364             if (!m_altText.isEmpty()) {
365                 String text = document().displayStringModifiedByEncoding(m_altText);
366                 context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace());
367                 const Font& font = style()->font();
368                 const FontMetrics& fontMetrics = font.fontMetrics();
369                 LayoutUnit ascent = fontMetrics.ascent();
370                 LayoutPoint altTextOffset = paintOffset;
371                 altTextOffset.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + ascent + (paddingHeight / 2) - borderWidth);
372
373                 // Only draw the alt text if it'll fit within the content box,
374                 // and only if it fits above the error image.
375                 TextRun textRun = RenderBlock::constructTextRun(this, font, text, style());
376                 LayoutUnit textWidth = font.width(textRun);
377                 if (errorPictureDrawn) {
378                     if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height())
379                         context->drawText(font, textRun, altTextOffset);
380                 } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height())
381                     context->drawText(font, textRun, altTextOffset);
382             }
383         }
384     } else if (m_imageResource->hasImage() && cWidth > 0 && cHeight > 0) {
385         RefPtr<Image> img = m_imageResource->image(cWidth, cHeight);
386         if (!img || img->isNull()) {
387             if (page && paintInfo.phase == PaintPhaseForeground)
388                 page->addRelevantUnpaintedObject(this, visualOverflowRect());
389             return;
390         }
391
392 #if PLATFORM(MAC)
393         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
394             paintCustomHighlight(toPoint(paintOffset - location()), style()->highlight(), true);
395 #endif
396
397         LayoutSize contentSize(cWidth, cHeight);
398         LayoutPoint contentLocation = paintOffset;
399         contentLocation.move(leftBorder + leftPad, topBorder + topPad);
400         paintIntoRect(context, LayoutRect(contentLocation, contentSize));
401         
402         if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) {
403             // For now, count images as unpainted if they are still progressively loading. We may want 
404             // to refine this in the future to account for the portion of the image that has painted.
405             if (cachedImage()->isLoading())
406                 page->addRelevantUnpaintedObject(this, LayoutRect(contentLocation, contentSize));
407             else
408                 page->addRelevantRepaintedObject(this, LayoutRect(contentLocation, contentSize));
409         }
410     }
411 }
412
413 void RenderImage::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
414 {
415     RenderReplaced::paint(paintInfo, paintOffset);
416     
417     if (paintInfo.phase == PaintPhaseOutline)
418         paintAreaElementFocusRing(paintInfo);
419 }
420     
421 void RenderImage::paintAreaElementFocusRing(PaintInfo& paintInfo)
422 {
423     if (document().printing() || !frame().selection().isFocusedAndActive())
424         return;
425     
426     if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
427         return;
428
429     Element* focusedElement = document().focusedElement();
430     if (!focusedElement || !isHTMLAreaElement(focusedElement))
431         return;
432
433     HTMLAreaElement* areaElement = toHTMLAreaElement(focusedElement);
434     if (areaElement->imageElement() != node())
435         return;
436
437     // Even if the theme handles focus ring drawing for entire elements, it won't do it for
438     // an area within an image, so we don't call RenderTheme::supportsFocusRing here.
439
440     Path path = areaElement->computePath(this);
441     if (path.isEmpty())
442         return;
443
444     // FIXME: Do we need additional code to clip the path to the image's bounding box?
445
446     RenderStyle* areaElementStyle = areaElement->computedStyle();
447     unsigned short outlineWidth = areaElementStyle->outlineWidth();
448     if (!outlineWidth)
449         return;
450
451     paintInfo.context->drawFocusRing(path, outlineWidth,
452         areaElementStyle->outlineOffset(),
453         areaElementStyle->visitedDependentColor(CSSPropertyOutlineColor));
454 }
455
456 void RenderImage::areaElementFocusChanged(HTMLAreaElement* element)
457 {
458     ASSERT_UNUSED(element, element->imageElement() == node());
459
460     // It would be more efficient to only repaint the focus ring rectangle
461     // for the passed-in area element. That would require adding functions
462     // to the area element class.
463     repaint();
464 }
465
466 void RenderImage::paintIntoRect(GraphicsContext* context, const LayoutRect& rect)
467 {
468     IntRect alignedRect = pixelSnappedIntRect(rect);
469     if (!m_imageResource->hasImage() || m_imageResource->errorOccurred() || alignedRect.width() <= 0 || alignedRect.height() <= 0)
470         return;
471
472     RefPtr<Image> img = m_imageResource->image(alignedRect.width(), alignedRect.height());
473     if (!img || img->isNull())
474         return;
475
476     HTMLImageElement* imageElt = (node() && isHTMLImageElement(node())) ? toHTMLImageElement(node()) : 0;
477     CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
478     Image* image = m_imageResource->image().get();
479     bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size());
480     ImageOrientationDescription orientationDescription;
481 #if ENABLE(CSS_IMAGE_ORIENTATION)
482     orientationDescription.setImageOrientationEnum(style()->imageOrientation());
483     orientationDescription.setRespectImageOrientation(shouldRespectImageOrientation());
484 #endif
485     context->drawImage(m_imageResource->image(alignedRect.width(), alignedRect.height()).get(), style()->colorSpace(), alignedRect, compositeOperator, orientationDescription, useLowQualityScaling);
486 }
487
488 bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox*) const
489 {
490     if (!RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(bleedAvoidance))
491         return false;
492
493     return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured();
494 }
495
496 bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
497 {
498     UNUSED_PARAM(maxDepthToTest);
499     if (!m_imageResource->hasImage() || m_imageResource->errorOccurred())
500         return false;
501     if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded())
502         return false;
503     if (!contentBoxRect().contains(localRect))
504         return false;
505     EFillBox backgroundClip = style()->backgroundClip();
506     // Background paints under borders.
507     if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground())
508         return false;
509     // Background shows in padding area.
510     if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
511         return false;
512     // Check for image with alpha.
513     return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this);
514 }
515
516 bool RenderImage::computeBackgroundIsKnownToBeObscured()
517 {
518     if (!hasBackground())
519         return false;
520     
521     LayoutRect paintedExtent;
522     if (!getBackgroundPaintedExtent(paintedExtent))
523         return false;
524     return foregroundIsKnownToBeOpaqueInRect(paintedExtent, 0);
525 }
526
527 LayoutUnit RenderImage::minimumReplacedHeight() const
528 {
529     return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit();
530 }
531
532 HTMLMapElement* RenderImage::imageMap() const
533 {
534     HTMLImageElement* i = node() && isHTMLImageElement(node()) ? toHTMLImageElement(node()) : 0;
535     return i ? i->treeScope()->getImageMap(i->fastGetAttribute(usemapAttr)) : 0;
536 }
537
538 bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
539 {
540     HitTestResult tempResult(result.hitTestLocation());
541     bool inside = RenderReplaced::nodeAtPoint(request, tempResult, locationInContainer, accumulatedOffset, hitTestAction);
542
543     if (tempResult.innerNode() && node()) {
544         if (HTMLMapElement* map = imageMap()) {
545             LayoutRect contentBox = contentBoxRect();
546             float scaleFactor = 1 / style()->effectiveZoom();
547             LayoutPoint mapLocation = locationInContainer.point() - toLayoutSize(accumulatedOffset) - locationOffset() - toLayoutSize(contentBox.location());
548             mapLocation.scale(scaleFactor, scaleFactor);
549
550             if (map->mapMouseEvent(mapLocation, contentBox.size(), tempResult))
551                 tempResult.setInnerNonSharedNode(node());
552         }
553     }
554
555     if (!inside && result.isRectBasedTest())
556         result.append(tempResult);
557     if (inside)
558         result = tempResult;
559     return inside;
560 }
561
562 void RenderImage::updateAltText()
563 {
564     if (!node())
565         return;
566
567     if (isHTMLInputElement(node()))
568         m_altText = toHTMLInputElement(node())->altText();
569     else if (isHTMLImageElement(node()))
570         m_altText = toHTMLImageElement(node())->altText();
571 }
572
573 void RenderImage::layout()
574 {
575     StackStats::LayoutCheckPoint layoutCheckPoint;
576     RenderReplaced::layout();
577
578     // Propagate container size to image resource.
579     IntSize containerSize(contentWidth(), contentHeight());
580     if (!containerSize.isEmpty())
581         m_imageResource->setContainerSizeForRenderer(containerSize);
582 }
583
584 void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
585 {
586     RenderReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize);
587
588     // Our intrinsicSize is empty if we're rendering generated images with relative width/height. Figure out the right intrinsic size to use.
589     if (intrinsicSize.isEmpty() && (m_imageResource->imageHasRelativeWidth() || m_imageResource->imageHasRelativeHeight())) {
590         RenderObject* containingBlock = isOutOfFlowPositioned() ? container() : this->containingBlock();
591         if (containingBlock->isBox()) {
592             RenderBox* box = toRenderBox(containingBlock);
593             intrinsicSize.setWidth(box->availableLogicalWidth());
594             intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding));
595         }
596     }
597     // Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image.
598     if (m_imageResource && m_imageResource->errorOccurred()) {
599         intrinsicRatio = 1;
600         return;
601     }
602 }
603
604 bool RenderImage::needsPreferredWidthsRecalculation() const
605 {
606     if (RenderReplaced::needsPreferredWidthsRecalculation())
607         return true;
608     return embeddedContentBox();
609 }
610
611 RenderBox* RenderImage::embeddedContentBox() const
612 {
613     if (!m_imageResource)
614         return 0;
615
616 #if ENABLE(SVG)
617     CachedImage* cachedImage = m_imageResource->cachedImage();
618     if (cachedImage && cachedImage->image() && cachedImage->image()->isSVGImage())
619         return static_cast<SVGImage*>(cachedImage->image())->embeddedContentBox();
620 #endif
621
622     return 0;
623 }
624
625 } // namespace WebCore