Diff
Modified: trunk/LayoutTests/ChangeLog (203377 => 203378)
--- trunk/LayoutTests/ChangeLog 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/LayoutTests/ChangeLog 2016-07-18 22:46:37 UTC (rev 203378)
@@ -1,3 +1,16 @@
+2016-07-18 Said Abou-Hallawa <sabouhall...@apple.com>
+
+ [iOS] PDFDocumentImage should cache only a sub image of the PDF when caching the whole image is expensive
+ https://bugs.webkit.org/show_bug.cgi?id=158715
+
+ Reviewed by Dean Jackson.
+
+ Make sure the PDF image will be displayed at the correct position if caching
+ the PDF image is disabled.
+
+ * fast/images/displaced-non-cached-pdf-expected.html: Added.
+ * fast/images/displaced-non-cached-pdf.html: Added.
+
2016-07-18 Chris Dumez <cdu...@apple.com>
The 2 first parameters to addEventListener() / removeEventListener() should be mandatory
Added: trunk/LayoutTests/fast/images/displaced-non-cached-pdf-expected.html (0 => 203378)
--- trunk/LayoutTests/fast/images/displaced-non-cached-pdf-expected.html (rev 0)
+++ trunk/LayoutTests/fast/images/displaced-non-cached-pdf-expected.html 2016-07-18 22:46:37 UTC (rev 203378)
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ display: inline-block;
+ background-color: rgb(0, 143, 0);
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+ <div></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/images/displaced-non-cached-pdf.html (0 => 203378)
--- trunk/LayoutTests/fast/images/displaced-non-cached-pdf.html (rev 0)
+++ trunk/LayoutTests/fast/images/displaced-non-cached-pdf.html 2016-07-18 22:46:37 UTC (rev 203378)
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+ <script>
+ if (window.testRunner && window.internals)
+ window.internals.settings.setCachedPDFImageEnabled(false);
+ </script>
+ <img src=""
+ <img src=""
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (203377 => 203378)
--- trunk/Source/WebCore/ChangeLog 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/ChangeLog 2016-07-18 22:46:37 UTC (rev 203378)
@@ -1,3 +1,63 @@
+2016-07-18 Said Abou-Hallawa <sabouhallawa@apple,com>
+
+ [iOS] PDFDocumentImage should cache only a sub image of the PDF when caching the whole image is expensive
+ https://bugs.webkit.org/show_bug.cgi?id=158715
+
+ Reviewed by Dean Jackson.
+
+ Test: fast/images/displaced-non-cached-pdf.html
+
+ For iOS, we need to ensure the size of the cached PDF images will not
+ exceed some limit. Also we should be caching only a sub image of the PDF
+ if caching the whole image will exceed the memory limit.
+
+ * page/Settings.cpp:
+ (WebCore::Settings::Settings):
+ (WebCore::Settings::setCachedPDFImageEnabled):
+ * page/Settings.h:
+ (WebCore::Settings::isCachedPDFImageEnabled):
+ Add an option to disable caching the PDF images.
+
+ * platform/graphics/cg/PDFDocumentImage.cpp:
+ (WebCore::PDFDocumentImage::setCachedPDFImageEnabled):
+ Allow the caller of draw() to disable caching the PDF images.
+
+ (WebCore::PDFDocumentImage::cacheParametersMatch):
+ Match the context dirty rectangle with the cached image rectangle.
+
+ (WebCore::transformContextForPainting):
+ When preparing the context for drawing the PDF, take the location
+ of the destination rectangle into account. We do not need to scale
+ the location of the source rectangle because we scale the size of
+ the rectangle but we don't scale the whole coordinate system.
+
+ (WebCore::cachedImageRect):
+ Calculate the rectangle of the cached image such that it does not
+ exceed the limit. Start from the center of the dirty rectangle and
+ then expand around it.
+
+ (WebCore::PDFDocumentImage::decodedSizeChanged):
+ In addition to notifying the ImageObserver, it keeps track of the size
+ of all the cached PDF images.
+
+ (WebCore::PDFDocumentImage::updateCachedImageIfNeeded):
+ Ensure the size of all the cached images does not exceed the limit
+
+ (WebCore::PDFDocumentImage::destroyDecodedData):
+ * platform/graphics/cg/PDFDocumentImage.h:
+
+ * rendering/RenderImage.cpp:
+ (WebCore::RenderImage::paintIntoRect):
+ Pass the option to disable caching the PDF images to PDFDocumentImage.
+
+ * testing/InternalSettings.cpp:
+ (WebCore::InternalSettings::Backup::Backup):
+ (WebCore::InternalSettings::Backup::restoreTo):
+ (WebCore::InternalSettings::setCachedPDFImageEnabled):
+ * testing/InternalSettings.h:
+ * testing/InternalSettings.idl:
+ Add an internal option to disable caching the PDF images.
+
2016-07-18 Chris Dumez <cdu...@apple.com>
The 2 first parameters to addEventListener() / removeEventListener() should be mandatory
Modified: trunk/Source/WebCore/page/Settings.cpp (203377 => 203378)
--- trunk/Source/WebCore/page/Settings.cpp 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/page/Settings.cpp 2016-07-18 22:46:37 UTC (rev 203378)
@@ -193,6 +193,7 @@
, m_loadsImagesAutomatically(false)
, m_areImagesEnabled(true)
, m_preferMIMETypeForImages(false)
+ , m_isCachedPDFImageEnabled(true)
, m_arePluginsEnabled(false)
, m_isScriptEnabled(false)
, m_needsAdobeFrameReloadingQuirk(false)
@@ -425,6 +426,11 @@
m_preferMIMETypeForImages = preferMIMETypeForImages;
}
+void Settings::setCachedPDFImageEnabled(bool isCachedPDFImageEnabled)
+{
+ m_isCachedPDFImageEnabled = isCachedPDFImageEnabled;
+}
+
void Settings::setForcePendingWebGLPolicy(bool forced)
{
m_forcePendingWebGLPolicy = forced;
Modified: trunk/Source/WebCore/page/Settings.h (203377 => 203378)
--- trunk/Source/WebCore/page/Settings.h 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/page/Settings.h 2016-07-18 22:46:37 UTC (rev 203378)
@@ -146,6 +146,9 @@
WEBCORE_EXPORT void setPreferMIMETypeForImages(bool);
bool preferMIMETypeForImages() const { return m_preferMIMETypeForImages; }
+ WEBCORE_EXPORT void setCachedPDFImageEnabled(bool);
+ bool isCachedPDFImageEnabled() const { return m_isCachedPDFImageEnabled; }
+
WEBCORE_EXPORT void setPluginsEnabled(bool);
bool arePluginsEnabled() const { return m_arePluginsEnabled; }
@@ -328,6 +331,7 @@
bool m_loadsImagesAutomatically : 1;
bool m_areImagesEnabled : 1;
bool m_preferMIMETypeForImages : 1;
+ bool m_isCachedPDFImageEnabled : 1;
bool m_arePluginsEnabled : 1;
bool m_isScriptEnabled : 1;
bool m_needsAdobeFrameReloadingQuirk : 1;
Modified: trunk/Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp (203377 => 203378)
--- trunk/Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp 2016-07-18 22:46:37 UTC (rev 203378)
@@ -100,6 +100,15 @@
return m_document; // Return true if size is available.
}
+void PDFDocumentImage::setCachedPDFImageEnabled(bool enabled)
+{
+ if (m_isCachedPDFImageEnabled == enabled)
+ return;
+
+ if (!(m_isCachedPDFImageEnabled = enabled))
+ destroyDecodedData();
+}
+
bool PDFDocumentImage::cacheParametersMatch(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect) const
{
if (dstRect.size() != m_cachedDestinationSize)
@@ -108,6 +117,9 @@
if (srcRect != m_cachedSourceRect)
return false;
+ if (!m_cachedImageRect.contains(context.clipBounds()))
+ return false;
+
AffineTransform::DecomposedType decomposedTransform;
context.getCTM(GraphicsContext::DefinitelyIncludeDeviceScale).decompose(decomposedTransform);
@@ -137,23 +149,59 @@
}
}
- context.translate(srcRect.x() * hScale, srcRect.y() * vScale);
+ // drawPDFPage() relies on drawing the whole PDF into a context starting at (0, 0). We need
+ // to transform the destination context such that srcRect of the source context will be drawn
+ // in dstRect of destination context.
+ context.translate(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y());
context.scale(FloatSize(hScale, -vScale));
context.translate(0, -srcRect.height());
}
+#if PLATFORM(IOS)
+// To avoid the jetsam on iOS, we are going to limit the size of all the PDF cachedImages to be 64MB.
+static const size_t s_maxCachedImageSide = 4 * 1024;
+static const size_t s_maxCachedImageArea = s_maxCachedImageSide * s_maxCachedImageSide;
+
+static const size_t s_maxDecodedDataSize = s_maxCachedImageArea * 4;
+static size_t s_allDecodedDataSize = 0;
+
+static FloatRect cachedImageRect(GraphicsContext& context, const FloatRect& dstRect)
+{
+ FloatSize maxSize = s_maxCachedImageSide / context.scaleFactor();
+
+ // Expand from the center of the dirty rectangle.
+ FloatRect rect = context.clipBounds();
+ rect.setX(std::max(rect.center().x() - maxSize.width() / 2, dstRect.x()));
+ rect.setY(std::max(rect.center().y() - maxSize.height() / 2, dstRect.y()));
+
+ // Cover as much as we could from the dstRect.
+ rect.setWidth(std::min(maxSize.width(), dstRect.width()));
+ rect.setHeight(std::min(maxSize.height(), dstRect.height()));
+ return rect;
+}
+#endif
+
+void PDFDocumentImage::decodedSizeChanged(size_t newCachedBytes)
+{
+ if (!m_cachedBytes && !newCachedBytes)
+ return;
+
+ if (imageObserver())
+ imageObserver()->decodedSizeChanged(this, -safeCast<int>(m_cachedBytes) + newCachedBytes);
+
+#if PLATFORM(IOS)
+ ASSERT(s_allDecodedDataSize >= m_cachedBytes);
+ // Update with the difference in two steps to avoid unsigned underflow subtraction.
+ s_allDecodedDataSize -= m_cachedBytes;
+ s_allDecodedDataSize += newCachedBytes;
+#endif
+
+ m_cachedBytes = newCachedBytes;
+}
+
void PDFDocumentImage::updateCachedImageIfNeeded(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect)
{
#if PLATFORM(IOS)
- // On iOS, if the physical memory is less than 1GB, do not allocate more than 16MB for the PDF cachedImage.
- const size_t memoryThreshold = WTF::GB;
- const size_t maxArea = 16 * WTF::MB / 4; // 16 MB maximum size, divided by a rough cost of 4 bytes per pixel of area.
-
- if (ramSize() <= memoryThreshold && ImageBuffer::compatibleBufferSize(dstRect.size(), context).area() >= maxArea) {
- m_cachedImageBuffer = nullptr;
- return;
- }
-
// On iOS, some clients use low-quality image interpolation always, which throws off this optimization,
// as we never get the subsequent high-quality paint. Since live resize is rare on iOS, disable the optimization.
// FIXME (136593): It's also possible to do the wrong thing here if CSS specifies low-quality interpolation via the "image-rendering"
@@ -167,15 +215,30 @@
bool repaintIfNecessary = interpolationQuality != InterpolationNone && interpolationQuality != InterpolationLow;
#endif
- if (m_cachedImageBuffer && (!repaintIfNecessary || cacheParametersMatch(context, dstRect, srcRect)))
+ if (!m_isCachedPDFImageEnabled || (m_cachedImageBuffer && (!repaintIfNecessary || cacheParametersMatch(context, dstRect, srcRect))))
return;
-
- m_cachedImageBuffer = ImageBuffer::createCompatibleBuffer(FloatRect(enclosingIntRect(dstRect)).size(), context);
- if (!m_cachedImageBuffer)
+
+#if PLATFORM(IOS)
+ m_cachedImageRect = cachedImageRect(context, dstRect);
+
+ // Cache the PDF image only if the size of the new image won't exceed the limit.
+ if (s_allDecodedDataSize + m_cachedImageRect.size().area() * 4 - m_cachedBytes > s_maxDecodedDataSize) {
+ destroyDecodedData();
return;
+ }
+#else
+ m_cachedImageRect = dstRect;
+#endif
+
+ m_cachedImageBuffer = ImageBuffer::createCompatibleBuffer(FloatRect(enclosingIntRect(m_cachedImageRect)).size(), context);
+ if (!m_cachedImageBuffer) {
+ destroyDecodedData();
+ return;
+ }
+
auto& bufferContext = m_cachedImageBuffer->context();
-
- transformContextForPainting(bufferContext, dstRect, srcRect);
+ // The PDF cachedImage should be drawn at (0, 0) always.
+ transformContextForPainting(bufferContext, FloatRect({ }, dstRect.size()), srcRect);
drawPDFPage(bufferContext);
m_cachedTransform = context.getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
@@ -183,11 +246,7 @@
m_cachedSourceRect = srcRect;
IntSize internalSize = m_cachedImageBuffer->internalSize();
- size_t oldCachedBytes = m_cachedBytes;
- m_cachedBytes = safeCast<size_t>(internalSize.width()) * internalSize.height() * 4;
-
- if (imageObserver())
- imageObserver()->decodedSizeChanged(this, safeCast<int>(m_cachedBytes) - safeCast<int>(oldCachedBytes));
+ decodedSizeChanged(safeCast<size_t>(internalSize.width()) * internalSize.height() * 4);
}
void PDFDocumentImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator op, BlendMode, ImageOrientationDescription)
@@ -216,11 +275,8 @@
void PDFDocumentImage::destroyDecodedData(bool)
{
m_cachedImageBuffer = nullptr;
-
- if (imageObserver())
- imageObserver()->decodedSizeChanged(this, -safeCast<int>(m_cachedBytes));
-
- m_cachedBytes = 0;
+ m_cachedImageRect = FloatRect();
+ decodedSizeChanged(0);
}
#if !USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
Modified: trunk/Source/WebCore/platform/graphics/cg/PDFDocumentImage.h (203377 => 203378)
--- trunk/Source/WebCore/platform/graphics/cg/PDFDocumentImage.h 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/platform/graphics/cg/PDFDocumentImage.h 2016-07-18 22:46:37 UTC (rev 203378)
@@ -52,6 +52,8 @@
return adoptRef(new PDFDocumentImage(observer));
}
+ void setCachedPDFImageEnabled(bool);
+
private:
PDFDocumentImage(ImageObserver*);
virtual ~PDFDocumentImage();
@@ -81,9 +83,12 @@
unsigned pageCount() const;
void drawPDFPage(GraphicsContext&);
+ void decodedSizeChanged(size_t newCachedBytes);
void updateCachedImageIfNeeded(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect);
bool cacheParametersMatch(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect) const;
+ bool m_isCachedPDFImageEnabled { true };
+
#if USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
RetainPtr<PDFDocument> m_document;
#else
@@ -91,6 +96,7 @@
#endif
std::unique_ptr<ImageBuffer> m_cachedImageBuffer;
+ FloatRect m_cachedImageRect;
AffineTransform m_cachedTransform;
FloatSize m_cachedDestinationSize;
FloatRect m_cachedSourceRect;
@@ -103,6 +109,8 @@
}
+SPECIALIZE_TYPE_TRAITS_IMAGE(PDFDocumentImage)
+
#endif // USE(CG)
#endif // PDFDocumentImage_h
Modified: trunk/Source/WebCore/rendering/RenderImage.cpp (203377 => 203378)
--- trunk/Source/WebCore/rendering/RenderImage.cpp 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/rendering/RenderImage.cpp 2016-07-18 22:46:37 UTC (rev 203378)
@@ -58,6 +58,11 @@
#include "SelectionRect.h"
#endif
+#if USE(CG)
+#include "PDFDocumentImage.h"
+#include "Settings.h"
+#endif
+
namespace WebCore {
#if PLATFORM(IOS)
@@ -545,6 +550,11 @@
Image* image = imageResource().image().get();
InterpolationQuality interpolation = image ? chooseInterpolationQuality(context, *image, image, LayoutSize(rect.size())) : InterpolationDefault;
+#if USE(CG)
+ if (is<PDFDocumentImage>(image))
+ downcast<PDFDocumentImage>(*image).setCachedPDFImageEnabled(frame().settings().isCachedPDFImageEnabled());
+#endif
+
ImageOrientationDescription orientationDescription(shouldRespectImageOrientation(), style().imageOrientation());
context.drawImage(*img, rect, ImagePaintingOptions(compositeOperator, BlendModeNormal, orientationDescription, interpolation));
}
Modified: trunk/Source/WebCore/testing/InternalSettings.cpp (203377 => 203378)
--- trunk/Source/WebCore/testing/InternalSettings.cpp 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/testing/InternalSettings.cpp 2016-07-18 22:46:37 UTC (rev 203378)
@@ -80,6 +80,7 @@
, m_langAttributeAwareFormControlUIEnabled(RuntimeEnabledFeatures::sharedFeatures().langAttributeAwareFormControlUIEnabled())
, m_imagesEnabled(settings.areImagesEnabled())
, m_preferMIMETypeForImages(settings.preferMIMETypeForImages())
+ , m_cachedPDFImageEnabled(settings.isCachedPDFImageEnabled())
, m_minimumTimerInterval(settings.minimumDOMTimerInterval())
#if ENABLE(VIDEO_TRACK)
, m_shouldDisplaySubtitles(settings.shouldDisplaySubtitles())
@@ -159,6 +160,7 @@
RuntimeEnabledFeatures::sharedFeatures().setLangAttributeAwareFormControlUIEnabled(m_langAttributeAwareFormControlUIEnabled);
settings.setImagesEnabled(m_imagesEnabled);
settings.setPreferMIMETypeForImages(m_preferMIMETypeForImages);
+ settings.setCachedPDFImageEnabled(m_cachedPDFImageEnabled);
settings.setMinimumDOMTimerInterval(m_minimumTimerInterval);
#if ENABLE(VIDEO_TRACK)
settings.setShouldDisplaySubtitles(m_shouldDisplaySubtitles);
@@ -483,6 +485,12 @@
settings()->setImagesEnabled(enabled);
}
+void InternalSettings::setCachedPDFImageEnabled(bool enabled, ExceptionCode& ec)
+{
+ InternalSettingsGuardForSettings();
+ settings()->setCachedPDFImageEnabled(enabled);
+}
+
void InternalSettings::setMinimumTimerInterval(double intervalInSeconds, ExceptionCode& ec)
{
InternalSettingsGuardForSettings();
Modified: trunk/Source/WebCore/testing/InternalSettings.h (203377 => 203378)
--- trunk/Source/WebCore/testing/InternalSettings.h 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/testing/InternalSettings.h 2016-07-18 22:46:37 UTC (rev 203378)
@@ -80,6 +80,7 @@
bool m_langAttributeAwareFormControlUIEnabled;
bool m_imagesEnabled;
bool m_preferMIMETypeForImages;
+ bool m_cachedPDFImageEnabled;
std::chrono::milliseconds m_minimumTimerInterval;
#if ENABLE(VIDEO_TRACK)
bool m_shouldDisplaySubtitles;
@@ -141,6 +142,7 @@
void setAllowsAirPlayForMediaPlayback(bool);
void setEditingBehavior(const String&, ExceptionCode&);
void setPreferMIMETypeForImages(bool, ExceptionCode&);
+ void setCachedPDFImageEnabled(bool, ExceptionCode&);
void setShouldDisplayTrackKind(const String& kind, bool enabled, ExceptionCode&);
bool shouldDisplayTrackKind(const String& kind, ExceptionCode&);
void setStorageBlockingPolicy(const String&, ExceptionCode&);
Modified: trunk/Source/WebCore/testing/InternalSettings.idl (203377 => 203378)
--- trunk/Source/WebCore/testing/InternalSettings.idl 2016-07-18 22:33:32 UTC (rev 203377)
+++ trunk/Source/WebCore/testing/InternalSettings.idl 2016-07-18 22:46:37 UTC (rev 203378)
@@ -68,6 +68,7 @@
// Other switches
[RaisesException] void setStorageBlockingPolicy(DOMString policy);
[RaisesException] void setImagesEnabled(boolean enabled);
+ [RaisesException] void setCachedPDFImageEnabled(boolean enabled);
[RaisesException] void setUseLegacyBackgroundSizeShorthandBehavior(boolean enabled);
[RaisesException] void setAutoscrollForDragAndDropEnabled(boolean enabled);
[RaisesException] void setBackgroundShouldExtendBeyondPage(boolean hasExtendedBackground);