1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2000 Dirk Mueller (mueller (at) kde.org) 5 * (C) 2006 Allan Sandfeld Jensen (kde (at) carewolf.com) 6 * (C) 2006 Samuel Weinig (sam.weinig (at) 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 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 68 RenderImage* RenderImage::createAnonymous(Document* document) 69 { 70 RenderImage* image = new RenderImage(0); 71 image->setDocumentForAnonymous(document); 72 return image; 73 } 74 75 RenderImage::~RenderImage() 76 { 77 ASSERT(m_imageResource); 78 m_imageResource->shutdown(); 79 } 80 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 97 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. 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 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 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 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 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 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 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 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 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 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 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 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 482 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 490 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 516 bool RenderImage::computeBackgroundIsKnownToBeObscured() 517 { 518 if (!hasBackground()) 519 return false; 520 return foregroundIsKnownToBeOpaqueInRect(backgroundPaintedExtent(), 0); 521 } 522 523 LayoutUnit RenderImage::minimumReplacedHeight() const 524 { 525 return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit(); 526 } 527 528 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 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 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 569 void RenderImage::layout() 570 { 571 LayoutRectRecorder recorder(*this); 572 RenderReplaced::layout(); 573 updateInnerContentRect(); 574 } 575 576 void RenderImage::didLayout(ResourceLoadPriorityOptimizer& optimizer) 577 { 578 RenderReplaced::didLayout(optimizer); 579 updateImageLoadingPriority(optimizer); 580 } 581 582 void RenderImage::didScroll(ResourceLoadPriorityOptimizer& optimizer) 583 { 584 RenderReplaced::didScroll(optimizer); 585 updateImageLoadingPriority(optimizer); 586 } 587 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 610 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 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 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