diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderImage.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderImage.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderImage.cpp | 500 |
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 |