summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderImage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderImage.cpp')
-rw-r--r--Source/WebCore/rendering/RenderImage.cpp500
1 files changed, 275 insertions, 225 deletions
diff --git a/Source/WebCore/rendering/RenderImage.cpp b/Source/WebCore/rendering/RenderImage.cpp
index 86e387ece..fc455e93d 100644
--- a/Source/WebCore/rendering/RenderImage.cpp
+++ b/Source/WebCore/rendering/RenderImage.cpp
@@ -28,12 +28,15 @@
#include "config.h"
#include "RenderImage.h"
+#include "AXObjectCache.h"
#include "BitmapImage.h"
#include "CachedImage.h"
-#include "Font.h"
+#include "FocusController.h"
#include "FontCache.h"
+#include "FontCascade.h"
#include "Frame.h"
#include "FrameSelection.h"
+#include "GeometryUtilities.h"
#include "GraphicsContext.h"
#include "HTMLAreaElement.h"
#include "HTMLImageElement.h"
@@ -44,6 +47,7 @@
#include "InlineElementBox.h"
#include "Page.h"
#include "PaintInfo.h"
+#include "RenderFlowThread.h"
#include "RenderImageResourceStyleImage.h"
#include "RenderView.h"
#include "SVGImage.h"
@@ -54,6 +58,11 @@
#include "SelectionRect.h"
#endif
+#if USE(CG)
+#include "PDFDocumentImage.h"
+#include "Settings.h"
+#endif
+
namespace WebCore {
#if PLATFORM(IOS)
@@ -103,43 +112,44 @@ void RenderImage::collectSelectionRects(Vector<SelectionRect>& rects, unsigned,
}
bool isFixed = false;
- IntRect absoluteBounds = localToAbsoluteQuad(FloatRect(imageRect), false, &isFixed).enclosingBoundingBox();
+ IntRect absoluteBounds = localToAbsoluteQuad(FloatRect(imageRect), UseTransforms, &isFixed).enclosingBoundingBox();
IntRect lineExtentBounds = localToAbsoluteQuad(FloatRect(lineExtentRect)).enclosingBoundingBox();
if (!containingBlock->isHorizontalWritingMode())
lineExtentBounds = lineExtentBounds.transposedRect();
// FIXME: We should consider either making SelectionRect a struct or better organize its optional fields into
// an auxiliary struct to simplify its initialization.
- rects.append(SelectionRect(absoluteBounds, containingBlock->style().direction(), lineExtentBounds.x(), lineExtentBounds.maxX(), lineExtentBounds.maxY(), 0, false /* line break */, isFirstOnLine, isLastOnLine, false /* contains start */, false /* contains end */, containingBlock->style().isHorizontalWritingMode(), isFixed, false /* ruby text */, columnNumberForOffset(absoluteBounds.x())));
+ rects.append(SelectionRect(absoluteBounds, containingBlock->style().direction(), lineExtentBounds.x(), lineExtentBounds.maxX(), lineExtentBounds.maxY(), 0, false /* line break */, isFirstOnLine, isLastOnLine, false /* contains start */, false /* contains end */, containingBlock->style().isHorizontalWritingMode(), isFixed, false /* ruby text */, view().pageNumberForBlockProgressionOffset(absoluteBounds.x())));
}
#endif
using namespace HTMLNames;
-RenderImage::RenderImage(Element& element, PassRef<RenderStyle> style, StyleImage* styleImage)
- : RenderReplaced(element, std::move(style), IntSize())
+RenderImage::RenderImage(Element& element, RenderStyle&& style, StyleImage* styleImage, const float imageDevicePixelRatio)
+ : RenderReplaced(element, WTFMove(style), IntSize())
, m_imageResource(styleImage ? std::make_unique<RenderImageResourceStyleImage>(*styleImage) : std::make_unique<RenderImageResource>())
- , m_needsToSetSizeForAltText(false)
- , m_didIncrementVisuallyNonEmptyPixelCount(false)
- , m_isGeneratedContent(false)
+ , m_imageDevicePixelRatio(imageDevicePixelRatio)
{
updateAltText();
- imageResource().initialize(this);
+ if (is<HTMLImageElement>(element))
+ m_hasShadowControls = downcast<HTMLImageElement>(element).hasShadowControls();
}
-RenderImage::RenderImage(Document& document, PassRef<RenderStyle> style, StyleImage* styleImage)
- : RenderReplaced(document, std::move(style), IntSize())
+RenderImage::RenderImage(Document& document, RenderStyle&& style, StyleImage* styleImage)
+ : RenderReplaced(document, WTFMove(style), IntSize())
, m_imageResource(styleImage ? std::make_unique<RenderImageResourceStyleImage>(*styleImage) : std::make_unique<RenderImageResource>())
- , m_needsToSetSizeForAltText(false)
- , m_didIncrementVisuallyNonEmptyPixelCount(false)
- , m_isGeneratedContent(false)
{
- imageResource().initialize(this);
}
RenderImage::~RenderImage()
{
+ // Do not add any code here. Add it to willBeDestroyed() instead.
+}
+
+void RenderImage::willBeDestroyed()
+{
imageResource().shutdown();
+ RenderReplaced::willBeDestroyed();
}
// If we'll be displaying either alt text or an image, add some padding.
@@ -156,10 +166,9 @@ IntSize RenderImage::imageSizeForError(CachedImage* newImage) const
ASSERT_ARG(newImage, newImage);
ASSERT_ARG(newImage, newImage->imageForRenderer(this));
- IntSize imageSize;
+ FloatSize imageSize;
if (newImage->willPaintBrokenImage()) {
- float deviceScaleFactor = WebCore::deviceScaleFactor(&frame());
- std::pair<Image*, float> brokenImageAndImageScaleFactor = newImage->brokenImage(deviceScaleFactor);
+ std::pair<Image*, float> brokenImageAndImageScaleFactor = newImage->brokenImage(document().deviceScaleFactor());
imageSize = brokenImageAndImageScaleFactor.first->size();
imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
} else
@@ -172,7 +181,7 @@ IntSize RenderImage::imageSizeForError(CachedImage* newImage) const
// Sets the image height and width to fit the alt text. Returns true if the
// image size changed.
-bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
+ImageSizeChangeType RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
{
IntSize imageSize;
if (newImage && newImage->imageForRenderer(this))
@@ -184,31 +193,36 @@ bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
// we have an alt and the user meant it (its not a text we invented)
if (!m_altText.isEmpty()) {
- FontCachePurgePreventer fontCachePurgePreventer;
-
- const Font& font = style().font();
- IntSize paddedTextSize(paddingWidth + std::min(ceilf(font.width(RenderBlock::constructTextRun(this, font, m_altText, style()))), maxAltTextWidth), paddingHeight + std::min(font.fontMetrics().height(), maxAltTextHeight));
+ const FontCascade& font = style().fontCascade();
+ IntSize paddedTextSize(paddingWidth + std::min(ceilf(font.width(RenderBlock::constructTextRun(m_altText, style()))), maxAltTextWidth), paddingHeight + std::min(font.fontMetrics().height(), maxAltTextHeight));
imageSize = imageSize.expandedTo(paddedTextSize);
}
if (imageSize == intrinsicSize())
- return false;
+ return ImageSizeChangeNone;
setIntrinsicSize(imageSize);
- return true;
+ return ImageSizeChangeForAltText;
+}
+
+void RenderImage::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
+{
+ if (!hasInitializedStyle())
+ imageResource().initialize(this);
+ RenderReplaced::styleWillChange(diff, newStyle);
}
void RenderImage::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderReplaced::styleDidChange(diff, oldStyle);
if (m_needsToSetSizeForAltText) {
- if (!m_altText.isEmpty() && setImageSizeForAltText(imageResource().cachedImage()))
- imageDimensionsChanged(true /* imageSizeChanged */);
+ if (!m_altText.isEmpty() && setImageSizeForAltText(cachedImage()))
+ repaintOrMarkForLayout(ImageSizeChangeForAltText);
m_needsToSetSizeForAltText = false;
}
#if ENABLE(CSS_IMAGE_ORIENTATION)
if (diff == StyleDifferenceLayout && oldStyle->imageOrientation() != style().imageOrientation())
- return imageDimensionsChanged(true /* imageSizeChanged */);
+ return repaintOrMarkForLayout(ImageSizeChangeNone);
#endif
#if ENABLE(CSS_IMAGE_RESOLUTION)
@@ -216,19 +230,16 @@ void RenderImage::styleDidChange(StyleDifference diff, const RenderStyle* oldSty
&& (oldStyle->imageResolution() != style().imageResolution()
|| oldStyle->imageResolutionSnap() != style().imageResolutionSnap()
|| oldStyle->imageResolutionSource() != style().imageResolutionSource()))
- imageDimensionsChanged(true /* imageSizeChanged */);
+ repaintOrMarkForLayout(ImageSizeChangeNone);
#endif
}
void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
{
- // FIXME (86669): Instead of the RenderImage determining whether its document is in the page
- // cache, the RenderImage should remove itself as a client when its document is put into the
- // page cache.
- if (documentBeingDestroyed() || document().inPageCache())
+ if (renderTreeBeingDestroyed())
return;
- if (hasBoxDecorations() || hasMask())
+ if (hasVisibleBoxDecorations() || hasMask() || hasShapeOutside())
RenderReplaced::imageChanged(newImage, rect);
if (newImage != imageResource().imagePtr() || !newImage)
@@ -240,7 +251,7 @@ void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
m_didIncrementVisuallyNonEmptyPixelCount = true;
}
- bool imageSizeChanged = false;
+ ImageSizeChangeType imageSizeChange = ImageSizeChangeNone;
// Set image dimensions, taking into account the size of the alt text.
if (imageResource().errorOccurred()) {
@@ -248,36 +259,37 @@ void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
ASSERT(element());
if (element()) {
m_needsToSetSizeForAltText = true;
- element()->setNeedsStyleRecalc(SyntheticStyleChange);
+ element()->invalidateStyleAndLayerComposition();
}
return;
}
- imageSizeChanged = setImageSizeForAltText(imageResource().cachedImage());
+ imageSizeChange = setImageSizeForAltText(cachedImage());
}
- imageDimensionsChanged(imageSizeChanged, rect);
+ if (UNLIKELY(AXObjectCache::accessibilityEnabled())) {
+ if (AXObjectCache* cache = document().existingAXObjectCache())
+ cache->recomputeIsIgnored(this);
+ }
+
+ repaintOrMarkForLayout(imageSizeChange, rect);
}
-bool RenderImage::updateIntrinsicSizeIfNeeded(const LayoutSize& newSize, bool imageSizeChanged)
+void RenderImage::updateIntrinsicSizeIfNeeded(const LayoutSize& newSize)
{
- if (newSize == intrinsicSize() && !imageSizeChanged)
- return false;
- if (imageResource().errorOccurred())
- return imageSizeChanged;
+ if (imageResource().errorOccurred() || !m_imageResource->hasImage())
+ return;
setIntrinsicSize(newSize);
- return true;
}
void RenderImage::updateInnerContentRect()
{
// Propagate container size to image resource.
- LayoutRect paintRect = replacedContentRect(intrinsicSize());
- IntSize containerSize(paintRect.width(), paintRect.height());
+ IntSize containerSize(replacedContentRect(intrinsicSize()).size());
if (!containerSize.isEmpty())
imageResource().setContainerSizeForRenderer(containerSize);
}
-void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* rect)
+void RenderImage::repaintOrMarkForLayout(ImageSizeChangeType imageSizeChange, const IntRect* rect)
{
#if ENABLE(CSS_IMAGE_RESOLUTION)
double scale = style().imageResolution();
@@ -285,10 +297,13 @@ void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* r
scale = roundForImpreciseConversion<int>(scale);
if (scale <= 0)
scale = 1;
- bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(imageResource().intrinsicSize(style().effectiveZoom() / scale), imageSizeChanged);
+ LayoutSize newIntrinsicSize = imageResource().intrinsicSize(style().effectiveZoom() / scale);
#else
- bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(imageResource().intrinsicSize(style().effectiveZoom()), imageSizeChanged);
+ LayoutSize newIntrinsicSize = imageResource().intrinsicSize(style().effectiveZoom());
#endif
+ LayoutSize oldIntrinsicSize = intrinsicSize();
+
+ updateIntrinsicSizeIfNeeded(newIntrinsicSize);
// In the case of generated image content using :before/:after/content, we might not be
// in the render tree yet. In that case, we just need to update our intrinsic size.
@@ -297,136 +312,96 @@ void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* r
if (!containingBlock())
return;
- bool shouldRepaint = true;
- if (intrinsicSizeChanged) {
- if (!preferredLogicalWidthsDirty())
- setPreferredLogicalWidthsDirty(true);
+ bool imageSourceHasChangedSize = oldIntrinsicSize != newIntrinsicSize || imageSizeChange != ImageSizeChangeNone;
- bool hasOverrideSize = hasOverrideHeight() || hasOverrideWidth();
- if (!hasOverrideSize && !imageSizeChanged) {
- LogicalExtentComputedValues computedValues;
- computeLogicalWidthInRegion(computedValues);
- LayoutUnit newWidth = computedValues.m_extent;
- computeLogicalHeight(height(), 0, computedValues);
- LayoutUnit newHeight = computedValues.m_extent;
-
- imageSizeChanged = width() != newWidth || height() != newHeight;
- }
-
- // FIXME: We only need to recompute the containing block's preferred size
- // if the containing block's size depends on the image's size (i.e., the container uses shrink-to-fit sizing).
- // There's no easy way to detect that shrink-to-fit is needed, always force a layout.
- bool containingBlockNeedsToRecomputePreferredSize =
- style().logicalWidth().isPercent()
- || style().logicalMaxWidth().isPercent()
- || style().logicalMinWidth().isPercent();
-
- if (imageSizeChanged || hasOverrideSize || containingBlockNeedsToRecomputePreferredSize) {
- shouldRepaint = false;
- if (!selfNeedsLayout())
- setNeedsLayout();
- }
+ if (imageSourceHasChangedSize && setNeedsLayoutIfNeededAfterIntrinsicSizeChange())
+ return;
- if (everHadLayout() && !selfNeedsLayout()) {
- // The inner content rectangle is calculated during layout, but may need an update now
- // (unless the box has already been scheduled for layout). In order to calculate it, we
- // may need values from the containing block, though, so make sure that we're not too
- // early. It may be that layout hasn't even taken place once yet.
+ if (everHadLayout() && !selfNeedsLayout()) {
+ // The inner content rectangle is calculated during layout, but may need an update now
+ // (unless the box has already been scheduled for layout). In order to calculate it, we
+ // may need values from the containing block, though, so make sure that we're not too
+ // early. It may be that layout hasn't even taken place once yet.
- // FIXME: we should not have to trigger another call to setContainerSizeForRenderer()
- // from here, since it's already being done during layout.
- updateInnerContentRect();
- }
+ // FIXME: we should not have to trigger another call to setContainerSizeForRenderer()
+ // from here, since it's already being done during layout.
+ updateInnerContentRect();
}
- if (shouldRepaint) {
- LayoutRect repaintRect;
- if (rect) {
- // The image changed rect is in source image coordinates (pre-zooming),
- // so map from the bounds of the image to the contentsBox.
- repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), imageResource().imageSize(1.0f)), contentBoxRect()));
- // Guard against too-large changed rects.
- repaintRect.intersect(contentBoxRect());
- } else
- repaintRect = contentBoxRect();
+ LayoutRect repaintRect = contentBoxRect();
+ if (rect) {
+ // The image changed rect is in source image coordinates (pre-zooming),
+ // so map from the bounds of the image to the contentsBox.
+ repaintRect.intersect(enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), imageResource().imageSize(1.0f)), repaintRect)));
+ }
- repaintRectangle(repaintRect);
+ repaintRectangle(repaintRect);
-#if USE(ACCELERATED_COMPOSITING)
- // Tell any potential compositing layers that the image needs updating.
- contentChanged(ImageChanged);
-#endif
- }
+ // Tell any potential compositing layers that the image needs updating.
+ contentChanged(ImageChanged);
}
-void RenderImage::notifyFinished(CachedResource* newImage)
+void RenderImage::notifyFinished(CachedResource& newImage)
{
- if (documentBeingDestroyed())
+ if (renderTreeBeingDestroyed())
return;
invalidateBackgroundObscurationStatus();
-#if USE(ACCELERATED_COMPOSITING)
- if (newImage == imageResource().cachedImage()) {
+ if (&newImage == cachedImage()) {
// tell any potential compositing layers
// that the image is done and they can reference it directly.
contentChanged(ImageChanged);
}
-#else
- UNUSED_PARAM(newImage);
-#endif
}
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
- LayoutUnit cWidth = contentWidth();
- LayoutUnit cHeight = contentHeight();
- LayoutUnit leftBorder = borderLeft();
- LayoutUnit topBorder = borderTop();
- LayoutUnit leftPad = paddingLeft();
- LayoutUnit topPad = paddingTop();
-
- GraphicsContext* context = paintInfo.context;
+ LayoutSize contentSize = this->contentSize();
- Page* page = frame().page();
+ GraphicsContext& context = paintInfo.context();
+ float deviceScaleFactor = document().deviceScaleFactor();
if (!imageResource().hasImage() || imageResource().errorOccurred()) {
if (paintInfo.phase == PaintPhaseSelection)
return;
- if (page && paintInfo.phase == PaintPhaseForeground)
- page->addRelevantUnpaintedObject(this, visualOverflowRect());
+ if (paintInfo.phase == PaintPhaseForeground)
+ page().addRelevantUnpaintedObject(this, visualOverflowRect());
+
+ if (contentSize.width() > 2 && contentSize.height() > 2) {
+ LayoutUnit borderWidth = LayoutUnit(1 / deviceScaleFactor);
- if (cWidth > 2 && cHeight > 2) {
- const int borderWidth = 1;
+ LayoutUnit leftBorder = borderLeft();
+ LayoutUnit topBorder = borderTop();
+ LayoutUnit leftPad = paddingLeft();
+ LayoutUnit topPad = paddingTop();
// Draw an outline rect where the image should be.
- context->setStrokeStyle(SolidStroke);
- context->setStrokeColor(Color::lightGray, style().colorSpace());
- context->setFillColor(Color::transparent, style().colorSpace());
- context->drawRect(pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight)));
+ context.setStrokeStyle(SolidStroke);
+ context.setStrokeColor(Color::lightGray);
+ context.setFillColor(Color::transparent);
+ context.drawRect(snapRectToDevicePixels(LayoutRect({ paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad }, contentSize), deviceScaleFactor), borderWidth);
bool errorPictureDrawn = false;
LayoutSize imageOffset;
// When calculating the usable dimensions, exclude the pixels of
// the ouline rect so the error image/alt text doesn't draw on it.
- LayoutUnit usableWidth = cWidth - 2 * borderWidth;
- LayoutUnit usableHeight = cHeight - 2 * borderWidth;
+ LayoutSize usableSize = contentSize - LayoutSize(2 * borderWidth, 2 * borderWidth);
RefPtr<Image> image = imageResource().image();
- if (imageResource().errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) {
- float deviceScaleFactor = WebCore::deviceScaleFactor(&frame());
+ if (imageResource().errorOccurred() && !image->isNull() && usableSize.width() >= image->width() && usableSize.height() >= image->height()) {
// Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution.
- std::pair<Image*, float> brokenImageAndImageScaleFactor = imageResource().cachedImage()->brokenImage(deviceScaleFactor);
+ std::pair<Image*, float> brokenImageAndImageScaleFactor = cachedImage()->brokenImage(document().deviceScaleFactor());
image = brokenImageAndImageScaleFactor.first;
- IntSize imageSize = image->size();
+ FloatSize imageSize = image->size();
imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
// Center the error image, accounting for border and padding.
- LayoutUnit centerX = (usableWidth - imageSize.width()) / 2;
+ LayoutUnit centerX = (usableSize.width() - imageSize.width()) / 2;
if (centerX < 0)
centerX = 0;
- LayoutUnit centerY = (usableHeight - imageSize.height()) / 2;
+ LayoutUnit centerY = (usableSize.height() - imageSize.height()) / 2;
if (centerY < 0)
centerY = 0;
imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth);
@@ -435,14 +410,14 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf
#if ENABLE(CSS_IMAGE_ORIENTATION)
orientationDescription.setImageOrientationEnum(style().imageOrientation());
#endif
- context->drawImage(image.get(), style().colorSpace(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, orientationDescription);
+ context.drawImage(*image, snapRectToDevicePixels(LayoutRect(paintOffset + imageOffset, imageSize), deviceScaleFactor), orientationDescription);
errorPictureDrawn = true;
}
if (!m_altText.isEmpty()) {
String text = document().displayStringModifiedByEncoding(m_altText);
- context->setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace());
- const Font& font = style().font();
+ context.setFillColor(style().visitedDependentColor(CSSPropertyColor));
+ const FontCascade& font = style().fontCascade();
const FontMetrics& fontMetrics = font.fontMetrics();
LayoutUnit ascent = fontMetrics.ascent();
LayoutPoint altTextOffset = paintOffset;
@@ -450,48 +425,47 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf
// Only draw the alt text if it'll fit within the content box,
// and only if it fits above the error image.
- TextRun textRun = RenderBlock::constructTextRun(this, font, text, style());
+ TextRun textRun = RenderBlock::constructTextRun(text, style());
LayoutUnit textWidth = font.width(textRun);
if (errorPictureDrawn) {
- if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height())
- context->drawText(font, textRun, altTextOffset);
- } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height())
- context->drawText(font, textRun, altTextOffset);
+ if (usableSize.width() >= textWidth && fontMetrics.height() <= imageOffset.height())
+ context.drawText(font, textRun, altTextOffset);
+ } else if (usableSize.width() >= textWidth && usableSize.height() >= fontMetrics.height())
+ context.drawText(font, textRun, altTextOffset);
}
}
- } else if (imageResource().hasImage() && cWidth > 0 && cHeight > 0) {
- RefPtr<Image> img = imageResource().image(cWidth, cHeight);
- if (!img || img->isNull()) {
- if (page && paintInfo.phase == PaintPhaseForeground)
- page->addRelevantUnpaintedObject(this, visualOverflowRect());
- return;
- }
+ return;
+ }
+
+ if (contentSize.isEmpty())
+ return;
-#if PLATFORM(MAC)
- if (style().highlight() != nullAtom && !paintInfo.context->paintingDisabled())
- paintCustomHighlight(toPoint(paintOffset - location()), style().highlight(), true);
-#endif
+ RefPtr<Image> img = imageResource().image(flooredIntSize(contentSize));
+ if (!img || img->isNull()) {
+ if (paintInfo.phase == PaintPhaseForeground)
+ page().addRelevantUnpaintedObject(this, visualOverflowRect());
+ return;
+ }
- LayoutRect contentRect = contentBoxRect();
- contentRect.moveBy(paintOffset);
- LayoutRect paintRect = replacedContentRect(intrinsicSize());
- paintRect.moveBy(paintOffset);
- bool clip = !contentRect.contains(paintRect);
- GraphicsContextStateSaver stateSaver(*context, clip);
- if (clip)
- context->clip(contentRect);
+ LayoutRect contentBoxRect = this->contentBoxRect();
+ contentBoxRect.moveBy(paintOffset);
+ LayoutRect replacedContentRect = this->replacedContentRect(intrinsicSize());
+ replacedContentRect.moveBy(paintOffset);
+ bool clip = !contentBoxRect.contains(replacedContentRect);
+ GraphicsContextStateSaver stateSaver(context, clip);
+ if (clip)
+ context.clip(contentBoxRect);
- paintIntoRect(context, paintRect);
-
- if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) {
- // For now, count images as unpainted if they are still progressively loading. We may want
- // to refine this in the future to account for the portion of the image that has painted.
- LayoutRect visibleRect = intersection(paintRect, contentRect);
- if (cachedImage()->isLoading())
- page->addRelevantUnpaintedObject(this, visibleRect);
- else
- page->addRelevantRepaintedObject(this, visibleRect);
- }
+ paintIntoRect(context, snapRectToDevicePixels(replacedContentRect, deviceScaleFactor));
+
+ if (cachedImage() && paintInfo.phase == PaintPhaseForeground) {
+ // For now, count images as unpainted if they are still progressively loading. We may want
+ // to refine this in the future to account for the portion of the image that has painted.
+ LayoutRect visibleRect = intersection(replacedContentRect, contentBoxRect);
+ if (cachedImage()->isLoading())
+ page().addRelevantUnpaintedObject(this, visibleRect);
+ else
+ page().addRelevantRepaintedObject(this, visibleRect);
}
}
@@ -500,45 +474,59 @@ void RenderImage::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
RenderReplaced::paint(paintInfo, paintOffset);
if (paintInfo.phase == PaintPhaseOutline)
- paintAreaElementFocusRing(paintInfo);
+ paintAreaElementFocusRing(paintInfo, paintOffset);
}
-void RenderImage::paintAreaElementFocusRing(PaintInfo& paintInfo)
+void RenderImage::paintAreaElementFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
#if PLATFORM(IOS)
UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(paintOffset);
#else
if (document().printing() || !frame().selection().isFocusedAndActive())
return;
- if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
+ if (paintInfo.context().paintingDisabled() && !paintInfo.context().updatingControlTints())
return;
Element* focusedElement = document().focusedElement();
- if (!focusedElement || !isHTMLAreaElement(focusedElement))
+ if (!is<HTMLAreaElement>(focusedElement))
+ return;
+
+ HTMLAreaElement& areaElement = downcast<HTMLAreaElement>(*focusedElement);
+ if (areaElement.imageElement() != element())
return;
- HTMLAreaElement* areaElement = toHTMLAreaElement(focusedElement);
- if (areaElement->imageElement() != element())
+ auto* areaElementStyle = areaElement.computedStyle();
+ if (!areaElementStyle)
+ return;
+
+ float outlineWidth = areaElementStyle->outlineWidth();
+ if (!outlineWidth)
return;
// Even if the theme handles focus ring drawing for entire elements, it won't do it for
// an area within an image, so we don't call RenderTheme::supportsFocusRing here.
-
- Path path = areaElement->computePath(this);
+ auto path = areaElement.computePathForFocusRing(size());
if (path.isEmpty())
return;
- // FIXME: Do we need additional code to clip the path to the image's bounding box?
+ AffineTransform zoomTransform;
+ zoomTransform.scale(style().effectiveZoom());
+ path.transform(zoomTransform);
- RenderStyle* areaElementStyle = areaElement->computedStyle();
- unsigned short outlineWidth = areaElementStyle->outlineWidth();
- if (!outlineWidth)
- return;
+ auto adjustedOffset = paintOffset;
+ adjustedOffset.moveBy(location());
+ path.translate(toFloatSize(adjustedOffset));
- paintInfo.context->drawFocusRing(path, outlineWidth,
- areaElementStyle->outlineOffset(),
- areaElementStyle->visitedDependentColor(CSSPropertyOutlineColor));
+#if PLATFORM(MAC)
+ bool needsRepaint;
+ paintInfo.context().drawFocusRing(path, page().focusController().timeSinceFocusWasSet(), needsRepaint);
+ if (needsRepaint)
+ page().focusController().setFocusedElementNeedsRepaint();
+#else
+ paintInfo.context().drawFocusRing(path, outlineWidth, areaElementStyle->outlineOffset(), areaElementStyle->visitedDependentColor(CSSPropertyOutlineColor));
+#endif
#endif
}
@@ -552,33 +540,37 @@ void RenderImage::areaElementFocusChanged(HTMLAreaElement* element)
repaint();
}
-void RenderImage::paintIntoRect(GraphicsContext* context, const LayoutRect& rect)
+void RenderImage::paintIntoRect(GraphicsContext& context, const FloatRect& rect)
{
- IntRect alignedRect = pixelSnappedIntRect(rect);
- if (!imageResource().hasImage() || imageResource().errorOccurred() || alignedRect.width() <= 0 || alignedRect.height() <= 0)
+ if (!imageResource().hasImage() || imageResource().errorOccurred() || rect.width() <= 0 || rect.height() <= 0)
return;
- RefPtr<Image> img = imageResource().image(alignedRect.width(), alignedRect.height());
+ RefPtr<Image> img = imageResource().image(flooredIntSize(rect.size()));
if (!img || img->isNull())
return;
- HTMLImageElement* imageElt = (element() && isHTMLImageElement(element())) ? toHTMLImageElement(element()) : 0;
- CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
+ HTMLImageElement* imageElement = is<HTMLImageElement>(element()) ? downcast<HTMLImageElement>(element()) : nullptr;
+ CompositeOperator compositeOperator = imageElement ? imageElement->compositeOperator() : CompositeSourceOver;
+
+ // FIXME: Document when image != img.get().
Image* image = imageResource().image().get();
- bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size());
- ImageOrientationDescription orientationDescription(shouldRespectImageOrientation());
-#if ENABLE(CSS_IMAGE_ORIENTATION)
- orientationDescription.setImageOrientationEnum(style().imageOrientation());
+ InterpolationQuality interpolation = image ? chooseInterpolationQuality(context, *image, image, LayoutSize(rect.size())) : InterpolationDefault;
+
+#if USE(CG)
+ if (is<PDFDocumentImage>(image))
+ downcast<PDFDocumentImage>(*image).setPdfImageCachingPolicy(settings().pdfImageCachingPolicy());
#endif
- context->drawImage(imageResource().image(alignedRect.width(), alignedRect.height()).get(), style().colorSpace(), alignedRect, compositeOperator, orientationDescription, useLowQualityScaling);
+
+ ImageOrientationDescription orientationDescription(shouldRespectImageOrientation(), style().imageOrientation());
+ context.drawImage(*img, rect, ImagePaintingOptions(compositeOperator, BlendModeNormal, orientationDescription, interpolation));
}
-bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox*) const
+bool RenderImage::boxShadowShouldBeAppliedToBackground(const LayoutPoint& paintOffset, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox*) const
{
- if (!RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(bleedAvoidance))
+ if (!RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(paintOffset, bleedAvoidance))
return false;
- return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured();
+ return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured(paintOffset);
}
bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
@@ -586,7 +578,7 @@ bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect,
UNUSED_PARAM(maxDepthToTest);
if (!imageResource().hasImage() || imageResource().errorOccurred())
return false;
- if (imageResource().cachedImage() && !imageResource().cachedImage()->isLoaded())
+ if (cachedImage() && !cachedImage()->isLoaded())
return false;
if (!contentBoxRect().contains(localRect))
return false;
@@ -601,17 +593,22 @@ bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect,
ObjectFit objectFit = style().objectFit();
if (objectFit != ObjectFitFill && objectFit != ObjectFitCover)
return false;
+
+ LengthPoint objectPosition = style().objectPosition();
+ if (objectPosition != RenderStyle::initialObjectPosition())
+ return false;
+
// Check for image with alpha.
- return imageResource().cachedImage() && imageResource().cachedImage()->currentFrameKnownToBeOpaque(this);
+ return cachedImage() && cachedImage()->currentFrameKnownToBeOpaque(this);
}
-bool RenderImage::computeBackgroundIsKnownToBeObscured()
+bool RenderImage::computeBackgroundIsKnownToBeObscured(const LayoutPoint& paintOffset)
{
if (!hasBackground())
return false;
LayoutRect paintedExtent;
- if (!getBackgroundPaintedExtent(paintedExtent))
+ if (!getBackgroundPaintedExtent(paintOffset, paintedExtent))
return false;
return foregroundIsKnownToBeOpaqueInRect(paintedExtent, 0);
}
@@ -623,8 +620,8 @@ LayoutUnit RenderImage::minimumReplacedHeight() const
HTMLMapElement* RenderImage::imageMap() const
{
- HTMLImageElement* i = element() && isHTMLImageElement(element()) ? toHTMLImageElement(element()) : 0;
- return i ? i->treeScope().getImageMap(i->fastGetAttribute(usemapAttr)) : 0;
+ HTMLImageElement* image = is<HTMLImageElement>(element()) ? downcast<HTMLImageElement>(element()) : nullptr;
+ return image ? image->treeScope().getImageMap(image->attributeWithoutSynchronization(usemapAttr)) : nullptr;
}
bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
@@ -637,7 +634,7 @@ bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu
LayoutRect contentBox = contentBoxRect();
float scaleFactor = 1 / style().effectiveZoom();
LayoutPoint mapLocation = locationInContainer.point() - toLayoutSize(accumulatedOffset) - locationOffset() - toLayoutSize(contentBox.location());
- mapLocation.scale(scaleFactor, scaleFactor);
+ mapLocation.scale(scaleFactor);
if (map->mapMouseEvent(mapLocation, contentBox.size(), tempResult))
tempResult.setInnerNonSharedNode(element());
@@ -656,30 +653,85 @@ void RenderImage::updateAltText()
if (!element())
return;
- if (isHTMLInputElement(element()))
- m_altText = toHTMLInputElement(element())->altText();
- else if (isHTMLImageElement(element()))
- m_altText = toHTMLImageElement(element())->altText();
+ if (is<HTMLInputElement>(*element()))
+ m_altText = downcast<HTMLInputElement>(*element()).altText();
+ else if (is<HTMLImageElement>(*element()))
+ m_altText = downcast<HTMLImageElement>(*element()).altText();
+}
+
+bool RenderImage::canHaveChildren() const
+{
+#if !ENABLE(SERVICE_CONTROLS)
+ return false;
+#else
+ return m_hasShadowControls;
+#endif
}
void RenderImage::layout()
{
StackStats::LayoutCheckPoint layoutCheckPoint;
+
+ LayoutSize oldSize = contentBoxRect().size();
RenderReplaced::layout();
+
updateInnerContentRect();
+
+ if (m_hasShadowControls)
+ layoutShadowControls(oldSize);
}
-void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
+void RenderImage::layoutShadowControls(const LayoutSize& oldSize)
{
- RenderReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize);
+ // We expect a single containing box under the UA shadow root.
+ ASSERT(firstChild() == lastChild());
+
+ auto* controlsRenderer = downcast<RenderBox>(firstChild());
+ if (!controlsRenderer)
+ return;
+
+ bool controlsNeedLayout = controlsRenderer->needsLayout();
+ // If the region chain has changed we also need to relayout the controls to update the region box info.
+ // FIXME: We can do better once we compute region box info for RenderReplaced, not only for RenderBlock.
+ const RenderFlowThread* flowThread = flowThreadContainingBlock();
+ if (flowThread && !controlsNeedLayout) {
+ if (flowThread->pageLogicalSizeChanged())
+ controlsNeedLayout = true;
+ }
+
+ LayoutSize newSize = contentBoxRect().size();
+ if (newSize == oldSize && !controlsNeedLayout)
+ return;
+
+ // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or
+ // instantiate LayoutStateDisabler. Since using a LayoutStateMaintainer is slightly more efficient,
+ // and this method might be called many times per second during video playback, use a LayoutStateMaintainer:
+ LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
+
+ if (shadowControlsNeedCustomLayoutMetrics()) {
+ controlsRenderer->setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop()));
+ controlsRenderer->mutableStyle().setHeight(Length(newSize.height(), Fixed));
+ controlsRenderer->mutableStyle().setWidth(Length(newSize.width(), Fixed));
+ }
+
+ controlsRenderer->setNeedsLayout(MarkOnlyThis);
+ controlsRenderer->layout();
+ clearChildNeedsLayout();
+
+ statePusher.pop();
+}
+
+void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio) const
+{
+ RenderReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio);
// Our intrinsicSize is empty if we're rendering generated images with relative width/height. Figure out the right intrinsic size to use.
if (intrinsicSize.isEmpty() && (imageResource().imageHasRelativeWidth() || imageResource().imageHasRelativeHeight())) {
RenderObject* containingBlock = isOutOfFlowPositioned() ? container() : this->containingBlock();
- if (containingBlock->isBox()) {
- RenderBox* box = toRenderBox(containingBlock);
- intrinsicSize.setWidth(box->availableLogicalWidth());
- intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding));
+ if (is<RenderBox>(*containingBlock)) {
+ auto& box = downcast<RenderBox>(*containingBlock);
+ intrinsicSize.setWidth(box.availableLogicalWidth());
+ intrinsicSize.setHeight(box.availableLogicalHeight(IncludeMarginBorderPadding));
}
}
// Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image.
@@ -698,13 +750,11 @@ bool RenderImage::needsPreferredWidthsRecalculation() const
RenderBox* RenderImage::embeddedContentBox() const
{
-#if ENABLE(SVG)
- CachedImage* cachedImage = imageResource().cachedImage();
- if (cachedImage && cachedImage->image() && cachedImage->image()->isSVGImage())
- return static_cast<SVGImage*>(cachedImage->image())->embeddedContentBox();
-#endif
+ CachedImage* cachedImage = this->cachedImage();
+ if (cachedImage && is<SVGImage>(cachedImage->image()))
+ return downcast<SVGImage>(*cachedImage->image()).embeddedContentBox();
- return 0;
+ return nullptr;
}
} // namespace WebCore