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 "core/rendering/RenderImage.h"
30
31 #include "HTMLNames.h"
32 #include "core/editing/FrameSelection.h"
33 #include "core/fetch/ImageResource.h"
34 #include "core/fetch/ResourceLoadPriorityOptimizer.h"
35 #include "core/fetch/ResourceLoader.h"
36 #include "core/html/HTMLAreaElement.h"
37 #include "core/html/HTMLImageElement.h"
38 #include "core/html/HTMLInputElement.h"
39 #include "core/html/HTMLMapElement.h"
40 #include "core/inspector/InspectorInstrumentation.h"
41 #include "core/frame/Frame.h"
42 #include "core/rendering/HitTestResult.h"
43 #include "core/rendering/LayoutRectRecorder.h"
44 #include "core/rendering/PaintInfo.h"
45 #include "core/rendering/RenderView.h"
46 #include "core/svg/graphics/SVGImage.h"
47 #include "platform/fonts/Font.h"
48 #include "platform/fonts/FontCache.h"
49 #include "platform/graphics/GraphicsContext.h"
50 #include "platform/graphics/GraphicsContextStateSaver.h"
51
52 using namespace std;
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
RenderImage(Element * element)58 RenderImage::RenderImage(Element* element)
59 : RenderReplaced(element, IntSize())
60 , m_needsToSetSizeForAltText(false)
61 , m_didIncrementVisuallyNonEmptyPixelCount(false)
62 , m_isGeneratedContent(false)
63 , m_imageDevicePixelRatio(1.0f)
64 {
65 updateAltText();
66 }
67
createAnonymous(Document * document)68 RenderImage* RenderImage::createAnonymous(Document* document)
69 {
70 RenderImage* image = new RenderImage(0);
71 image->setDocumentForAnonymous(document);
72 return image;
73 }
74
~RenderImage()75 RenderImage::~RenderImage()
76 {
77 ASSERT(m_imageResource);
78 m_imageResource->shutdown();
79 }
80
setImageResource(PassOwnPtr<RenderImageResource> imageResource)81 void RenderImage::setImageResource(PassOwnPtr<RenderImageResource> imageResource)
82 {
83 ASSERT(!m_imageResource);
84 m_imageResource = imageResource;
85 m_imageResource->initialize(this);
86 }
87
88 // If we'll be displaying either alt text or an image, add some padding.
89 static const unsigned short paddingWidth = 4;
90 static const unsigned short paddingHeight = 4;
91
92 // Alt text is restricted to this maximum size, in pixels. These are
93 // signed integers because they are compared with other signed values.
94 static const float maxAltTextWidth = 1024;
95 static const int maxAltTextHeight = 256;
96
imageSizeForError(ImageResource * newImage) const97 IntSize RenderImage::imageSizeForError(ImageResource* newImage) const
98 {
99 ASSERT_ARG(newImage, newImage);
100 ASSERT_ARG(newImage, newImage->imageForRenderer(this));
101
102 IntSize imageSize;
103 if (newImage->willPaintBrokenImage()) {
104 float deviceScaleFactor = WebCore::deviceScaleFactor(frame());
105 pair<Image*, float> brokenImageAndImageScaleFactor = ImageResource::brokenImage(deviceScaleFactor);
106 imageSize = brokenImageAndImageScaleFactor.first->size();
107 imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
108 } else
109 imageSize = newImage->imageForRenderer(this)->size();
110
111 // imageSize() returns 0 for the error image. We need the true size of the
112 // error image, so we have to get it by grabbing image() directly.
113 return IntSize(paddingWidth + imageSize.width() * style()->effectiveZoom(), paddingHeight + imageSize.height() * style()->effectiveZoom());
114 }
115
116 // Sets the image height and width to fit the alt text. Returns true if the
117 // image size changed.
setImageSizeForAltText(ImageResource * newImage)118 bool RenderImage::setImageSizeForAltText(ImageResource* newImage /* = 0 */)
119 {
120 IntSize imageSize;
121 if (newImage && newImage->imageForRenderer(this))
122 imageSize = imageSizeForError(newImage);
123 else if (!m_altText.isEmpty() || newImage) {
124 // If we'll be displaying either text or an image, add a little padding.
125 imageSize = IntSize(paddingWidth, paddingHeight);
126 }
127
128 // we have an alt and the user meant it (its not a text we invented)
129 if (!m_altText.isEmpty()) {
130 FontCachePurgePreventer fontCachePurgePreventer;
131
132 const Font& font = style()->font();
133 IntSize paddedTextSize(paddingWidth + min(ceilf(font.width(RenderBlockFlow::constructTextRun(this, font, m_altText, style()))), maxAltTextWidth), paddingHeight + min(font.fontMetrics().height(), maxAltTextHeight));
134 imageSize = imageSize.expandedTo(paddedTextSize);
135 }
136
137 if (imageSize == intrinsicSize())
138 return false;
139
140 setIntrinsicSize(imageSize);
141 return true;
142 }
143
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)144 void RenderImage::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
145 {
146 RenderReplaced::styleDidChange(diff, oldStyle);
147 if (m_needsToSetSizeForAltText) {
148 if (!m_altText.isEmpty() && setImageSizeForAltText(m_imageResource->cachedImage()))
149 imageDimensionsChanged(true /* imageSizeChanged */);
150 m_needsToSetSizeForAltText = false;
151 }
152 }
153
imageChanged(WrappedImagePtr newImage,const IntRect * rect)154 void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
155 {
156 if (documentBeingDestroyed())
157 return;
158
159 if (hasBoxDecorations() || hasMask())
160 RenderReplaced::imageChanged(newImage, rect);
161
162 if (!m_imageResource)
163 return;
164
165 if (newImage != m_imageResource->imagePtr())
166 return;
167
168 // Per the spec, we let the server-sent header override srcset/other sources of dpr.
169 // https://github.com/igrigorik/http-client-hints/blob/master/draft-grigorik-http-client-hints-01.txt#L255
170 if (m_imageResource->cachedImage() && m_imageResource->cachedImage()->hasDevicePixelRatioHeaderValue())
171 m_imageDevicePixelRatio = 1 / m_imageResource->cachedImage()->devicePixelRatioHeaderValue();
172
173 if (!m_didIncrementVisuallyNonEmptyPixelCount) {
174 // At a zoom level of 1 the image is guaranteed to have an integer size.
175 view()->frameView()->incrementVisuallyNonEmptyPixelCount(flooredIntSize(m_imageResource->imageSize(1.0f)));
176 m_didIncrementVisuallyNonEmptyPixelCount = true;
177 }
178
179 bool imageSizeChanged = false;
180
181 // Set image dimensions, taking into account the size of the alt text.
182 if (m_imageResource->errorOccurred() || !newImage) {
183 if (!m_altText.isEmpty() && document().hasPendingStyleRecalc()) {
184 ASSERT(node());
185 if (node()) {
186 m_needsToSetSizeForAltText = true;
187 node()->setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer);
188 }
189 return;
190 }
191 imageSizeChanged = setImageSizeForAltText(m_imageResource->cachedImage());
192 }
193
194 imageDimensionsChanged(imageSizeChanged, rect);
195 }
196
updateIntrinsicSizeIfNeeded(const LayoutSize & newSize,bool imageSizeChanged)197 bool RenderImage::updateIntrinsicSizeIfNeeded(const LayoutSize& newSize, bool imageSizeChanged)
198 {
199 if (newSize == intrinsicSize() && !imageSizeChanged)
200 return false;
201 if (m_imageResource->errorOccurred() || !m_imageResource->hasImage())
202 return imageSizeChanged;
203 setIntrinsicSize(newSize);
204 return true;
205 }
206
updateInnerContentRect()207 void RenderImage::updateInnerContentRect()
208 {
209 // Propagate container size to the image resource.
210 LayoutRect containerRect = replacedContentRect();
211 IntSize containerSize(containerRect.width(), containerRect.height());
212 if (!containerSize.isEmpty())
213 m_imageResource->setContainerSizeForRenderer(containerSize);
214 }
215
imageDimensionsChanged(bool imageSizeChanged,const IntRect * rect)216 void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* rect)
217 {
218 bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->intrinsicSize(style()->effectiveZoom()), imageSizeChanged);
219
220 // In the case of generated image content using :before/:after/content, we might not be
221 // in the render tree yet. In that case, we just need to update our intrinsic size.
222 // layout() will be called after we are inserted in the tree which will take care of
223 // what we are doing here.
224 if (!containingBlock())
225 return;
226
227 bool shouldRepaint = true;
228 if (intrinsicSizeChanged) {
229 if (!preferredLogicalWidthsDirty())
230 setPreferredLogicalWidthsDirty();
231
232 bool hasOverrideSize = hasOverrideHeight() || hasOverrideWidth();
233 if (!hasOverrideSize && !imageSizeChanged) {
234 LogicalExtentComputedValues computedValues;
235 computeLogicalWidthInRegion(computedValues);
236 LayoutUnit newWidth = computedValues.m_extent;
237 computeLogicalHeight(height(), 0, computedValues);
238 LayoutUnit newHeight = computedValues.m_extent;
239
240 imageSizeChanged = width() != newWidth || height() != newHeight;
241 }
242
243 // FIXME: We only need to recompute the containing block's preferred size
244 // if the containing block's size depends on the image's size (i.e., the container uses shrink-to-fit sizing).
245 // There's no easy way to detect that shrink-to-fit is needed, always force a layout.
246 bool containingBlockNeedsToRecomputePreferredSize =
247 style()->logicalWidth().isPercent()
248 || style()->logicalMaxWidth().isPercent()
249 || style()->logicalMinWidth().isPercent();
250
251 if (imageSizeChanged || hasOverrideSize || containingBlockNeedsToRecomputePreferredSize) {
252 shouldRepaint = false;
253 if (!selfNeedsLayout())
254 setNeedsLayout();
255 }
256 }
257
258 if (everHadLayout() && !selfNeedsLayout()) {
259 // The inner content rectangle is calculated during layout, but may need an update now
260 // (unless the box has already been scheduled for layout). In order to calculate it, we
261 // may need values from the containing block, though, so make sure that we're not too
262 // early. It may be that layout hasn't even taken place once yet.
263 updateInnerContentRect();
264 }
265
266 if (shouldRepaint) {
267 LayoutRect repaintRect;
268 if (rect) {
269 // The image changed rect is in source image coordinates (pre-zooming),
270 // so map from the bounds of the image to the contentsBox.
271 repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), m_imageResource->imageSize(1.0f)), contentBoxRect()));
272 // Guard against too-large changed rects.
273 repaintRect.intersect(contentBoxRect());
274 } else
275 repaintRect = contentBoxRect();
276
277 repaintRectangle(repaintRect);
278
279 // Tell any potential compositing layers that the image needs updating.
280 contentChanged(ImageChanged);
281 }
282 }
283
notifyFinished(Resource * newImage)284 void RenderImage::notifyFinished(Resource* newImage)
285 {
286 if (!m_imageResource)
287 return;
288
289 if (documentBeingDestroyed())
290 return;
291
292 invalidateBackgroundObscurationStatus();
293
294 if (newImage == m_imageResource->cachedImage()) {
295 // tell any potential compositing layers
296 // that the image is done and they can reference it directly.
297 contentChanged(ImageChanged);
298 }
299 }
300
paintReplaced(PaintInfo & paintInfo,const LayoutPoint & paintOffset)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 if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) {
313 if (paintInfo.phase == PaintPhaseSelection)
314 return;
315
316 if (cWidth > 2 && cHeight > 2) {
317 const int borderWidth = 1;
318
319 // Draw an outline rect where the image should be.
320 context->setStrokeStyle(SolidStroke);
321 context->setStrokeColor(Color::lightGray);
322 context->setFillColor(Color::transparent);
323 context->drawRect(pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight)));
324
325 bool errorPictureDrawn = false;
326 LayoutSize imageOffset;
327 // When calculating the usable dimensions, exclude the pixels of
328 // the ouline rect so the error image/alt text doesn't draw on it.
329 LayoutUnit usableWidth = cWidth - 2 * borderWidth;
330 LayoutUnit usableHeight = cHeight - 2 * borderWidth;
331
332 RefPtr<Image> image = m_imageResource->image();
333
334 if (m_imageResource->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) {
335 float deviceScaleFactor = WebCore::deviceScaleFactor(frame());
336 // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution.
337 pair<Image*, float> brokenImageAndImageScaleFactor = ImageResource::brokenImage(deviceScaleFactor);
338 image = brokenImageAndImageScaleFactor.first;
339 IntSize imageSize = image->size();
340 imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
341 // Center the error image, accounting for border and padding.
342 LayoutUnit centerX = (usableWidth - imageSize.width()) / 2;
343 if (centerX < 0)
344 centerX = 0;
345 LayoutUnit centerY = (usableHeight - imageSize.height()) / 2;
346 if (centerY < 0)
347 centerY = 0;
348 imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth);
349 context->drawImage(image.get(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, shouldRespectImageOrientation());
350 errorPictureDrawn = true;
351 }
352
353 if (!m_altText.isEmpty()) {
354 const Font& font = style()->font();
355 const FontMetrics& fontMetrics = font.fontMetrics();
356 LayoutUnit ascent = fontMetrics.ascent();
357 LayoutPoint textRectOrigin = paintOffset;
358 textRectOrigin.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + (paddingHeight / 2) - borderWidth);
359 LayoutPoint textOrigin(textRectOrigin.x(), textRectOrigin.y() + ascent);
360
361 // Only draw the alt text if it'll fit within the content box,
362 // and only if it fits above the error image.
363 TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_altText, style(), TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, DefaultTextRunFlags | RespectDirection);
364 LayoutUnit textWidth = font.width(textRun);
365 TextRunPaintInfo textRunPaintInfo(textRun);
366 textRunPaintInfo.bounds = FloatRect(textRectOrigin, FloatSize(textWidth, fontMetrics.height()));
367 context->setFillColor(resolveColor(CSSPropertyColor));
368 if (errorPictureDrawn) {
369 if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height())
370 context->drawText(font, textRunPaintInfo, textOrigin);
371 } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height())
372 context->drawText(font, textRunPaintInfo, textOrigin);
373 }
374 }
375 } else if (m_imageResource->hasImage() && cWidth > 0 && cHeight > 0) {
376 RefPtr<Image> img = m_imageResource->image(cWidth, cHeight);
377 if (!img || img->isNull())
378 return;
379
380 LayoutRect contentRect = contentBoxRect();
381 contentRect.moveBy(paintOffset);
382 LayoutRect paintRect = replacedContentRect();
383 paintRect.moveBy(paintOffset);
384 bool clip = !contentRect.contains(paintRect);
385 if (clip) {
386 context->save();
387 context->clip(contentRect);
388 }
389
390 paintIntoRect(context, paintRect);
391
392 if (clip)
393 context->restore();
394 }
395 }
396
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)397 void RenderImage::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
398 {
399 RenderReplaced::paint(paintInfo, paintOffset);
400
401 if (paintInfo.phase == PaintPhaseOutline)
402 paintAreaElementFocusRing(paintInfo);
403 }
404
paintAreaElementFocusRing(PaintInfo & paintInfo)405 void RenderImage::paintAreaElementFocusRing(PaintInfo& paintInfo)
406 {
407 Document& document = this->document();
408
409 if (document.printing() || !document.frame()->selection().isFocusedAndActive())
410 return;
411
412 if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
413 return;
414
415 Element* focusedElement = document.focusedElement();
416 if (!focusedElement || !isHTMLAreaElement(focusedElement))
417 return;
418
419 HTMLAreaElement* areaElement = toHTMLAreaElement(focusedElement);
420 if (areaElement->imageElement() != node())
421 return;
422
423 // Even if the theme handles focus ring drawing for entire elements, it won't do it for
424 // an area within an image, so we don't call RenderTheme::supportsFocusRing here.
425
426 Path path = areaElement->computePath(this);
427 if (path.isEmpty())
428 return;
429
430 RenderStyle* areaElementStyle = areaElement->computedStyle();
431 unsigned short outlineWidth = areaElementStyle->outlineWidth();
432 if (!outlineWidth)
433 return;
434
435 // FIXME: Clip path instead of context when Skia pathops is ready.
436 // https://crbug.com/251206
437 GraphicsContextStateSaver savedContext(*paintInfo.context);
438 paintInfo.context->clip(absoluteContentBox());
439 paintInfo.context->drawFocusRing(path, outlineWidth,
440 areaElementStyle->outlineOffset(),
441 resolveColor(areaElementStyle, CSSPropertyOutlineColor));
442 }
443
areaElementFocusChanged(HTMLAreaElement * areaElement)444 void RenderImage::areaElementFocusChanged(HTMLAreaElement* areaElement)
445 {
446 ASSERT(areaElement->imageElement() == node());
447
448 Path path = areaElement->computePath(this);
449 if (path.isEmpty())
450 return;
451
452 RenderStyle* areaElementStyle = areaElement->computedStyle();
453 unsigned short outlineWidth = areaElementStyle->outlineWidth();
454
455 IntRect repaintRect = enclosingIntRect(path.boundingRect());
456 repaintRect.moveBy(-absoluteContentBox().location());
457 repaintRect.inflate(outlineWidth);
458
459 repaintRectangle(repaintRect);
460 }
461
paintIntoRect(GraphicsContext * context,const LayoutRect & rect)462 void RenderImage::paintIntoRect(GraphicsContext* context, const LayoutRect& rect)
463 {
464 IntRect alignedRect = pixelSnappedIntRect(rect);
465 if (!m_imageResource->hasImage() || m_imageResource->errorOccurred() || alignedRect.width() <= 0 || alignedRect.height() <= 0)
466 return;
467
468 RefPtr<Image> img = m_imageResource->image(alignedRect.width(), alignedRect.height());
469 if (!img || img->isNull())
470 return;
471
472 HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? toHTMLImageElement(node()) : 0;
473 CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
474 Image* image = m_imageResource->image().get();
475 bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size());
476
477 InspectorInstrumentation::willPaintImage(this);
478 context->drawImage(m_imageResource->image(alignedRect.width(), alignedRect.height()).get(), alignedRect, compositeOperator, shouldRespectImageOrientation(), useLowQualityScaling);
479 InspectorInstrumentation::didPaintImage(this);
480 }
481
boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance,InlineFlowBox *) const482 bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox*) const
483 {
484 if (!RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(bleedAvoidance))
485 return false;
486
487 return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured();
488 }
489
foregroundIsKnownToBeOpaqueInRect(const LayoutRect & localRect,unsigned) const490 bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned) const
491 {
492 if (!m_imageResource->hasImage() || m_imageResource->errorOccurred())
493 return false;
494 if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded())
495 return false;
496 if (!contentBoxRect().contains(localRect))
497 return false;
498 EFillBox backgroundClip = style()->backgroundClip();
499 // Background paints under borders.
500 if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground())
501 return false;
502 // Background shows in padding area.
503 if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
504 return false;
505 // Object-position may leave parts of the content box empty, regardless of the value of object-fit.
506 if (style()->objectPosition() != RenderStyle::initialObjectPosition())
507 return false;
508 // Object-fit may leave parts of the content box empty.
509 ObjectFit objectFit = style()->objectFit();
510 if (objectFit != ObjectFitFill && objectFit != ObjectFitCover)
511 return false;
512 // Check for image with alpha.
513 return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this);
514 }
515
computeBackgroundIsKnownToBeObscured()516 bool RenderImage::computeBackgroundIsKnownToBeObscured()
517 {
518 if (!hasBackground())
519 return false;
520 return foregroundIsKnownToBeOpaqueInRect(backgroundPaintedExtent(), 0);
521 }
522
minimumReplacedHeight() const523 LayoutUnit RenderImage::minimumReplacedHeight() const
524 {
525 return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit();
526 }
527
imageMap() const528 HTMLMapElement* RenderImage::imageMap() const
529 {
530 HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? toHTMLImageElement(node()) : 0;
531 return i ? i->treeScope().getImageMap(i->fastGetAttribute(usemapAttr)) : 0;
532 }
533
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)534 bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
535 {
536 HitTestResult tempResult(result.hitTestLocation());
537 bool inside = RenderReplaced::nodeAtPoint(request, tempResult, locationInContainer, accumulatedOffset, hitTestAction);
538
539 if (tempResult.innerNode() && node()) {
540 if (HTMLMapElement* map = imageMap()) {
541 LayoutRect contentBox = contentBoxRect();
542 float scaleFactor = 1 / style()->effectiveZoom();
543 LayoutPoint mapLocation = locationInContainer.point() - toLayoutSize(accumulatedOffset) - locationOffset() - toLayoutSize(contentBox.location());
544 mapLocation.scale(scaleFactor, scaleFactor);
545
546 if (map->mapMouseEvent(mapLocation, contentBox.size(), tempResult))
547 tempResult.setInnerNonSharedNode(node());
548 }
549 }
550
551 if (!inside && result.isRectBasedTest())
552 result.append(tempResult);
553 if (inside)
554 result = tempResult;
555 return inside;
556 }
557
updateAltText()558 void RenderImage::updateAltText()
559 {
560 if (!node())
561 return;
562
563 if (node()->hasTagName(inputTag))
564 m_altText = toHTMLInputElement(node())->altText();
565 else if (node()->hasTagName(imgTag))
566 m_altText = toHTMLImageElement(node())->altText();
567 }
568
layout()569 void RenderImage::layout()
570 {
571 LayoutRectRecorder recorder(*this);
572 RenderReplaced::layout();
573 updateInnerContentRect();
574 }
575
didLayout(ResourceLoadPriorityOptimizer & optimizer)576 void RenderImage::didLayout(ResourceLoadPriorityOptimizer& optimizer)
577 {
578 RenderReplaced::didLayout(optimizer);
579 updateImageLoadingPriority(optimizer);
580 }
581
didScroll(ResourceLoadPriorityOptimizer & optimizer)582 void RenderImage::didScroll(ResourceLoadPriorityOptimizer& optimizer)
583 {
584 RenderReplaced::didScroll(optimizer);
585 updateImageLoadingPriority(optimizer);
586 }
587
updateImageLoadingPriority(ResourceLoadPriorityOptimizer & optimizer)588 void RenderImage::updateImageLoadingPriority(ResourceLoadPriorityOptimizer& optimizer)
589 {
590 if (!m_imageResource || !m_imageResource->cachedImage() || m_imageResource->cachedImage()->isLoaded())
591 return;
592
593 LayoutRect viewBounds = viewRect();
594 LayoutRect objectBounds = absoluteContentBox();
595
596 // The object bounds might be empty right now, so intersects will fail since it doesn't deal
597 // with empty rects. Use LayoutRect::contains in that case.
598 bool isVisible;
599 if (!objectBounds.isEmpty())
600 isVisible = viewBounds.intersects(objectBounds);
601 else
602 isVisible = viewBounds.contains(objectBounds);
603
604 ResourceLoadPriorityOptimizer::VisibilityStatus status = isVisible ?
605 ResourceLoadPriorityOptimizer::Visible : ResourceLoadPriorityOptimizer::NotVisible;
606
607 optimizer.notifyImageResourceVisibility(m_imageResource->cachedImage(), status);
608 }
609
computeIntrinsicRatioInformation(FloatSize & intrinsicSize,double & intrinsicRatio,bool & isPercentageIntrinsicSize) const610 void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
611 {
612 RenderReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize);
613
614 // Our intrinsicSize is empty if we're rendering generated images with relative width/height. Figure out the right intrinsic size to use.
615 if (intrinsicSize.isEmpty() && (m_imageResource->imageHasRelativeWidth() || m_imageResource->imageHasRelativeHeight())) {
616 RenderObject* containingBlock = isOutOfFlowPositioned() ? container() : this->containingBlock();
617 if (containingBlock->isBox()) {
618 RenderBox* box = toRenderBox(containingBlock);
619 intrinsicSize.setWidth(box->availableLogicalWidth());
620 intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding));
621 }
622 }
623 // Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image.
624 // Video is excluded from this behavior because video elements have a default aspect ratio that a failed poster image load should not override.
625 if (m_imageResource && m_imageResource->errorOccurred() && !isVideo()) {
626 intrinsicRatio = 1;
627 return;
628 }
629 }
630
needsPreferredWidthsRecalculation() const631 bool RenderImage::needsPreferredWidthsRecalculation() const
632 {
633 if (RenderReplaced::needsPreferredWidthsRecalculation())
634 return true;
635 return embeddedContentBox();
636 }
637
embeddedContentBox() const638 RenderBox* RenderImage::embeddedContentBox() const
639 {
640 if (!m_imageResource)
641 return 0;
642
643 ImageResource* cachedImage = m_imageResource->cachedImage();
644 if (cachedImage && cachedImage->image() && cachedImage->image()->isSVGImage())
645 return toSVGImage(cachedImage->image())->embeddedContentBox();
646
647 return 0;
648 }
649
650 } // namespace WebCore
651