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