summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLCanvasElement.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/html/HTMLCanvasElement.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/html/HTMLCanvasElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLCanvasElement.cpp567
1 files changed, 312 insertions, 255 deletions
diff --git a/Source/WebCore/html/HTMLCanvasElement.cpp b/Source/WebCore/html/HTMLCanvasElement.cpp
index 518a34233..7d7d3911a 100644
--- a/Source/WebCore/html/HTMLCanvasElement.cpp
+++ b/Source/WebCore/html/HTMLCanvasElement.cpp
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,84 +28,98 @@
#include "config.h"
#include "HTMLCanvasElement.h"
-#include "Attribute.h"
#include "CanvasGradient.h"
#include "CanvasPattern.h"
#include "CanvasRenderingContext2D.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
#include "Document.h"
#include "ExceptionCode.h"
#include "Frame.h"
#include "FrameLoaderClient.h"
+#include "GeometryUtilities.h"
#include "GraphicsContext.h"
#include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
#include "ImageData.h"
#include "MIMETypeRegistry.h"
-#include "MainFrame.h"
-#include "Page.h"
#include "RenderHTMLCanvas.h"
#include "ScriptController.h"
#include "Settings.h"
#include <math.h>
-
+#include <runtime/JSCInlines.h>
#include <runtime/JSLock.h>
-#include <runtime/Operations.h>
+#include <wtf/RAMSize.h>
+#include <wtf/text/StringBuilder.h>
#if ENABLE(WEBGL)
#include "WebGLContextAttributes.h"
-#include "WebGLRenderingContext.h"
+#include "WebGLRenderingContextBase.h"
#endif
namespace WebCore {
using namespace HTMLNames;
-// These values come from the WhatWG spec.
-static const int DefaultWidth = 300;
-static const int DefaultHeight = 150;
+// These values come from the WhatWG/W3C HTML spec.
+const int defaultWidth = 300;
+const int defaultHeight = 150;
// Firefox limits width/height to 32767 pixels, but slows down dramatically before it
// reaches that limit. We limit by area instead, giving us larger maximum dimensions,
-// in exchange for a smaller maximum canvas size.
-#if !PLATFORM(IOS)
-static const float MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels
+// in exchange for a smaller maximum canvas size. The maximum canvas size is in device pixels.
+#if PLATFORM(IOS)
+const unsigned maxCanvasArea = 4096 * 4096;
+#elif PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
+const unsigned maxCanvasArea = 8192 * 8192;
+#else
+const unsigned maxCanvasArea = 16384 * 16384;
#endif
+#if USE(CG)
+// FIXME: It seems strange that the default quality is not the one that is literally named "default".
+// Should fix names to make this easier to understand, or write an excellent comment here explaining why not.
+const InterpolationQuality defaultInterpolationQuality = InterpolationLow;
+#else
+const InterpolationQuality defaultInterpolationQuality = InterpolationDefault;
+#endif
+
+static size_t activePixelMemory = 0;
+
HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document& document)
: HTMLElement(tagName, document)
- , m_size(DefaultWidth, DefaultHeight)
- , m_rendererIsCanvas(false)
- , m_ignoreReset(false)
- , m_deviceScaleFactor(targetDeviceScaleFactor())
- , m_originClean(true)
-#if PLATFORM(IOS)
- // FIXME: We should look to reconcile usage of MaxCanvasArea and m_maximumDecodedImageSize.
- , m_maximumDecodedImageSize(document.settings() ? document.settings()->maximumDecodedImageSize() : 0)
-#endif
- , m_hasCreatedImageBuffer(false)
- , m_didClearImageBuffer(false)
+ , m_size(defaultWidth, defaultHeight)
{
ASSERT(hasTagName(canvasTag));
- setHasCustomStyleResolveCallbacks();
}
-PassRefPtr<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
+Ref<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
{
- return adoptRef(new HTMLCanvasElement(canvasTag, document));
+ return adoptRef(*new HTMLCanvasElement(canvasTag, document));
}
-PassRefPtr<HTMLCanvasElement> HTMLCanvasElement::create(const QualifiedName& tagName, Document& document)
+Ref<HTMLCanvasElement> HTMLCanvasElement::create(const QualifiedName& tagName, Document& document)
{
- return adoptRef(new HTMLCanvasElement(tagName, document));
+ return adoptRef(*new HTMLCanvasElement(tagName, document));
}
+static void removeFromActivePixelMemory(size_t pixelsReleased)
+{
+ if (!pixelsReleased)
+ return;
+
+ if (pixelsReleased < activePixelMemory)
+ activePixelMemory -= pixelsReleased;
+ else
+ activePixelMemory = 0;
+}
+
HTMLCanvasElement::~HTMLCanvasElement()
{
- for (auto it = m_observers.begin(), end = m_observers.end(); it != end; ++it)
- (*it)->canvasDestroyed(*this);
+ for (auto& observer : m_observers)
+ observer->canvasDestroyed(*this);
+
+ m_context = nullptr; // Ensure this goes away before the ImageBuffer.
- m_context.clear(); // Ensure this goes away before the ImageBuffer.
+ releaseImageBufferAndContext();
}
void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
@@ -115,26 +129,12 @@ void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicSt
HTMLElement::parseAttribute(name, value);
}
-RenderPtr<RenderElement> HTMLCanvasElement::createElementRenderer(PassRef<RenderStyle> style)
+RenderPtr<RenderElement> HTMLCanvasElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
{
Frame* frame = document().frame();
- if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript)) {
- m_rendererIsCanvas = true;
- return createRenderer<RenderHTMLCanvas>(*this, std::move(style));
- }
-
- m_rendererIsCanvas = false;
- return HTMLElement::createElementRenderer(std::move(style));
-}
-
-void HTMLCanvasElement::willAttachRenderers()
-{
- setIsInCanvasSubtree(true);
-}
-
-bool HTMLCanvasElement::areAuthorShadowsAllowed() const
-{
- return false;
+ if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
+ return createRenderer<RenderHTMLCanvas>(*this, WTFMove(style));
+ return HTMLElement::createElementRenderer(WTFMove(style), insertionPosition);
}
bool HTMLCanvasElement::canContainRangeEndPoint() const
@@ -157,126 +157,131 @@ void HTMLCanvasElement::removeObserver(CanvasObserver& observer)
m_observers.remove(&observer);
}
-void HTMLCanvasElement::setHeight(int value)
+void HTMLCanvasElement::setHeight(unsigned value)
{
- setIntegralAttribute(heightAttr, value);
+ setAttributeWithoutSynchronization(heightAttr, AtomicString::number(limitToOnlyHTMLNonNegative(value, defaultHeight)));
}
-void HTMLCanvasElement::setWidth(int value)
+void HTMLCanvasElement::setWidth(unsigned value)
{
- setIntegralAttribute(widthAttr, value);
+ setAttributeWithoutSynchronization(widthAttr, AtomicString::number(limitToOnlyHTMLNonNegative(value, defaultWidth)));
}
-#if ENABLE(WEBGL)
-static bool requiresAcceleratedCompositingForWebGL()
+static inline size_t maxActivePixelMemory()
{
-#if PLATFORM(GTK) || PLATFORM(EFL)
- return false;
-#else
- return true;
-#endif
-
+ static size_t maxPixelMemory;
+ static std::once_flag onceFlag;
+ std::call_once(onceFlag, [] {
+ maxPixelMemory = std::max(ramSize() / 4, 2151 * MB);
+ });
+ return maxPixelMemory;
}
-static bool shouldEnableWebGL(Settings* settings)
+
+CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type)
{
- if (!settings)
- return false;
+ if (HTMLCanvasElement::is2dType(type))
+ return getContext2d(type);
- if (!settings->webGLEnabled())
- return false;
+#if ENABLE(WEBGL)
+ if (HTMLCanvasElement::is3dType(type))
+ return getContextWebGL(type);
+#endif
- if (!requiresAcceleratedCompositingForWebGL())
- return true;
+ return nullptr;
+}
- return settings->acceleratedCompositingEnabled();
+bool HTMLCanvasElement::is2dType(const String& type)
+{
+ return type == "2d";
}
-#endif
-CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, CanvasContextAttributes* attrs)
+CanvasRenderingContext* HTMLCanvasElement::getContext2d(const String& type)
{
- // A Canvas can either be "2D" or "webgl" but never both. If you request a 2D canvas and the existing
- // context is already 2D, just return that. If the existing context is WebGL, then destroy it
- // before creating a new 2D context. Vice versa when requesting a WebGL canvas. Requesting a
- // context with any other type string will destroy any existing context.
-
- // FIXME: The code depends on the context not going away once created, to prevent JS from
- // seeing a dangling pointer. So for now we will disallow the context from being changed
- // once it is created. https://bugs.webkit.org/show_bug.cgi?id=117095
- if (is2dType(type)) {
- if (m_context && !m_context->is2d())
- return nullptr;
- if (!m_context) {
- bool usesDashbardCompatibilityMode = false;
+ ASSERT_UNUSED(HTMLCanvasElement::is2dType(type), type);
+
+ if (m_context && !m_context->is2d())
+ return nullptr;
+ if (!m_context) {
+ bool usesDashboardCompatibilityMode = false;
#if ENABLE(DASHBOARD_SUPPORT)
- if (Settings* settings = document().settings())
- usesDashbardCompatibilityMode = settings->usesDashboardBackwardCompatibilityMode();
-#endif
- m_context = CanvasRenderingContext2D::create(this, document().inQuirksMode(), usesDashbardCompatibilityMode);
-#if USE(IOSURFACE_CANVAS_BACKING_STORE) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING))
- // Need to make sure a RenderLayer and compositing layer get created for the Canvas
- setNeedsStyleRecalc(SyntheticStyleChange);
+ usesDashboardCompatibilityMode = document().settings().usesDashboardBackwardCompatibilityMode();
#endif
+
+ // Make sure we don't use more pixel memory than the system can support.
+ size_t requestedPixelMemory = 4 * width() * height();
+ if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
+ stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
+ stringBuilder.appendLiteral(" MB).");
+ document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
+ return nullptr;
}
- return m_context.get();
+
+ m_context = std::make_unique<CanvasRenderingContext2D>(*this, document().inQuirksMode(), usesDashboardCompatibilityMode);
+
+ downcast<CanvasRenderingContext2D>(*m_context).setUsesDisplayListDrawing(m_usesDisplayListDrawing);
+ downcast<CanvasRenderingContext2D>(*m_context).setTracksDisplayListReplay(m_tracksDisplayListReplay);
+
+#if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
+ // Need to make sure a RenderLayer and compositing layer get created for the Canvas
+ invalidateStyleAndLayerComposition();
+#endif
}
+
+ return m_context.get();
+}
+
#if ENABLE(WEBGL)
- if (shouldEnableWebGL(document().settings())) {
-
- if (is3dType(type)) {
- if (m_context && !m_context->is3d())
- return nullptr;
- if (!m_context) {
- Page* page = document().page();
- if (page && !document().url().isLocalFile()) {
- WebGLLoadPolicy policy = page->mainFrame().loader().client().webGLPolicyForURL(document().url());
-
- if (policy == WebGLBlock)
- return nullptr;
- }
- m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs));
- if (m_context) {
- // Need to make sure a RenderLayer and compositing layer get created for the Canvas
- setNeedsStyleRecalc(SyntheticStyleChange);
- }
- }
- return m_context.get();
- }
- }
+static bool requiresAcceleratedCompositingForWebGL()
+{
+#if PLATFORM(GTK)
+ return false;
#else
- UNUSED_PARAM(attrs);
+ return true;
#endif
- return nullptr;
+
}
-
-bool HTMLCanvasElement::probablySupportsContext(const String& type, CanvasContextAttributes*)
+static bool shouldEnableWebGL(const Settings& settings)
{
- // FIXME: Provide implementation that accounts for attributes. Bugzilla bug 117093
- // https://bugs.webkit.org/show_bug.cgi?id=117093
+ if (!settings.webGLEnabled())
+ return false;
- // FIXME: The code depends on the context not going away once created (as getContext
- // is implemented under this assumption) https://bugs.webkit.org/show_bug.cgi?id=117095
- if (is2dType(type))
- return !m_context || m_context->is2d();
+ if (!requiresAcceleratedCompositingForWebGL())
+ return true;
-#if ENABLE(WEBGL)
- if (shouldEnableWebGL(document().settings())) {
- if (is3dType(type))
- return !m_context || m_context->is3d();
- }
-#endif
- return false;
+ return settings.acceleratedCompositingEnabled();
}
-bool HTMLCanvasElement::is2dType(const String& type)
+bool HTMLCanvasElement::is3dType(const String& type)
{
- return type == "2d";
+ // Retain support for the legacy "webkit-3d" name.
+ return type == "webgl" || type == "experimental-webgl"
+#if ENABLE(WEBGL2)
+ || type == "webgl2"
+#endif
+ || type == "webkit-3d";
}
-#if ENABLE(WEBGL)
-bool HTMLCanvasElement::is3dType(const String& type)
+CanvasRenderingContext* HTMLCanvasElement::getContextWebGL(const String& type, WebGLContextAttributes&& attrs)
{
- // Retain support for the legacy "webkit-3d" name.
- return type == "webgl" || type == "experimental-webgl" || type == "webkit-3d";
+ ASSERT(HTMLCanvasElement::is3dType(type));
+
+ if (!shouldEnableWebGL(document().settings()))
+ return nullptr;
+
+ if (m_context && !m_context->is3d())
+ return nullptr;
+
+ if (!m_context) {
+ m_context = WebGLRenderingContextBase::create(*this, attrs, type);
+ if (m_context) {
+ // Need to make sure a RenderLayer and compositing layer get created for the Canvas
+ invalidateStyleAndLayerComposition();
+ }
+ }
+
+ return m_context.get();
}
#endif
@@ -284,9 +289,13 @@ void HTMLCanvasElement::didDraw(const FloatRect& rect)
{
clearCopiedImage();
+ FloatRect dirtyRect = rect;
if (RenderBox* ro = renderBox()) {
FloatRect destRect = ro->contentBoxRect();
- FloatRect r = mapRect(rect, FloatRect(0, 0, size().width(), size().height()), destRect);
+ // Inflate dirty rect to cover antialiasing on image buffers.
+ if (drawingContext() && drawingContext()->shouldAntialias())
+ dirtyRect.inflate(1);
+ FloatRect r = mapRect(dirtyRect, FloatRect(0, 0, size().width(), size().height()), destRect);
r.intersect(destRect);
if (r.isEmpty() || m_dirtyRect.contains(r))
return;
@@ -294,14 +303,13 @@ void HTMLCanvasElement::didDraw(const FloatRect& rect)
m_dirtyRect.unite(r);
ro->repaintRectangle(enclosingIntRect(m_dirtyRect));
}
-
- notifyObserversCanvasChanged(rect);
+ notifyObserversCanvasChanged(dirtyRect);
}
void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect)
{
- for (auto it = m_observers.begin(), end = m_observers.end(); it != end; ++it)
- (*it)->canvasChanged(*this, rect);
+ for (auto& observer : m_observers)
+ observer->canvasChanged(*this, rect);
}
void HTMLCanvasElement::reset()
@@ -309,16 +317,10 @@ void HTMLCanvasElement::reset()
if (m_ignoreReset)
return;
- bool ok;
bool hadImageBuffer = hasCreatedImageBuffer();
- int w = getAttribute(widthAttr).toInt(&ok);
- if (!ok || w < 0)
- w = DefaultWidth;
-
- int h = getAttribute(heightAttr).toInt(&ok);
- if (!ok || h < 0)
- h = DefaultHeight;
+ int w = limitToOnlyHTMLNonNegative(attributeWithoutSynchronization(widthAttr), defaultWidth);
+ int h = limitToOnlyHTMLNonNegative(attributeWithoutSynchronization(heightAttr), defaultHeight);
if (m_contextStateSaver) {
// Reset to the initial graphics context state.
@@ -326,57 +328,40 @@ void HTMLCanvasElement::reset()
m_contextStateSaver->save();
}
- if (m_context && m_context->is2d()) {
- CanvasRenderingContext2D* context2D = static_cast<CanvasRenderingContext2D*>(m_context.get());
- context2D->reset();
- }
+ if (is<CanvasRenderingContext2D>(m_context.get()))
+ downcast<CanvasRenderingContext2D>(*m_context).reset();
IntSize oldSize = size();
IntSize newSize(w, h);
- float newDeviceScaleFactor = targetDeviceScaleFactor();
-
// If the size of an existing buffer matches, we can just clear it instead of reallocating.
// This optimization is only done for 2D canvases for now.
- if (m_hasCreatedImageBuffer && oldSize == newSize && m_deviceScaleFactor == newDeviceScaleFactor && m_context && m_context->is2d()) {
+ if (m_hasCreatedImageBuffer && oldSize == newSize && m_context && m_context->is2d()) {
if (!m_didClearImageBuffer)
clearImageBuffer();
return;
}
- m_deviceScaleFactor = newDeviceScaleFactor;
-
setSurfaceSize(newSize);
#if ENABLE(WEBGL)
- if (m_context && m_context->is3d() && oldSize != size())
- static_cast<WebGLRenderingContext*>(m_context.get())->reshape(width(), height());
+ if (is3D() && oldSize != size())
+ static_cast<WebGLRenderingContextBase*>(m_context.get())->reshape(width(), height());
#endif
- if (auto renderer = this->renderer()) {
- if (m_rendererIsCanvas) {
- if (oldSize != size()) {
- toRenderHTMLCanvas(renderer)->canvasSizeChanged();
-#if USE(ACCELERATED_COMPOSITING)
- if (renderBox() && renderBox()->hasAcceleratedCompositing())
- renderBox()->contentChanged(CanvasChanged);
-#endif
- }
- if (hadImageBuffer)
- renderer->repaint();
+ auto renderer = this->renderer();
+ if (is<RenderHTMLCanvas>(renderer)) {
+ auto& canvasRenderer = downcast<RenderHTMLCanvas>(*renderer);
+ if (oldSize != size()) {
+ canvasRenderer.canvasSizeChanged();
+ if (canvasRenderer.hasAcceleratedCompositing())
+ canvasRenderer.contentChanged(CanvasChanged);
}
+ if (hadImageBuffer)
+ canvasRenderer.repaint();
}
- for (auto it = m_observers.begin(), end = m_observers.end(); it != end; ++it)
- (*it)->canvasResized(*this);
-}
-
-float HTMLCanvasElement::targetDeviceScaleFactor() const
-{
-#if ENABLE(HIGH_DPI_CANVAS)
- return document().frame() ? document().frame()->page()->deviceScaleFactor() : 1;
-#else
- return 1;
-#endif
+ for (auto& observer : m_observers)
+ observer->canvasResized(*this);
}
bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
@@ -387,28 +372,28 @@ bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
return true;
#endif
-#if USE(ACCELERATED_COMPOSITING)
if (!m_context->isAccelerated())
return true;
if (renderBox() && renderBox()->hasAcceleratedCompositing())
return false;
-#endif
+
return true;
}
-void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r, bool useLowQualityScale)
+void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
{
// Clear the dirty rect
m_dirtyRect = FloatRect();
- if (context->paintingDisabled())
+ if (context.paintingDisabled())
return;
if (m_context) {
if (!paintsIntoCanvasBuffer() && !document().printing())
return;
+
m_context->paintRenderingResultsToCanvas();
}
@@ -420,15 +405,15 @@ void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r, boo
#if ENABLE(CSS_IMAGE_ORIENTATION)
orientationDescription.setImageOrientationEnum(renderer()->style().imageOrientation());
#endif
- context->drawImage(m_presentedImage.get(), ColorSpaceDeviceRGB, pixelSnappedIntRect(r), CompositeSourceOver, orientationDescription, useLowQualityScale);
+ context.drawImage(*m_presentedImage, snappedIntRect(r), ImagePaintingOptions(orientationDescription));
} else
- context->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, pixelSnappedIntRect(r), CompositeSourceOver, BlendModeNormal, useLowQualityScale);
+ context.drawImageBuffer(*imageBuffer, snappedIntRect(r));
}
}
#if ENABLE(WEBGL)
if (is3D())
- static_cast<WebGLRenderingContext*>(m_context.get())->markLayerComposited();
+ static_cast<WebGLRenderingContextBase*>(m_context.get())->markLayerComposited();
#endif
}
@@ -455,72 +440,68 @@ void HTMLCanvasElement::makePresentationCopy()
void HTMLCanvasElement::clearPresentationCopy()
{
- m_presentedImage.clear();
+ m_presentedImage = nullptr;
}
+void HTMLCanvasElement::releaseImageBufferAndContext()
+{
+ m_contextStateSaver = nullptr;
+ setImageBuffer(nullptr);
+}
+
void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
{
m_size = size;
m_hasCreatedImageBuffer = false;
- m_contextStateSaver.clear();
- m_imageBuffer.reset();
+ releaseImageBufferAndContext();
clearCopiedImage();
}
String HTMLCanvasElement::toEncodingMimeType(const String& mimeType)
{
- String lowercaseMimeType = mimeType.lower();
-
- // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread).
- if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType))
- lowercaseMimeType = "image/png";
-
- return lowercaseMimeType;
+ if (!MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
+ return ASCIILiteral("image/png");
+ return mimeType.convertToASCIILowercase();
}
-String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionCode& ec)
+ExceptionOr<String> HTMLCanvasElement::toDataURL(const String& mimeType, std::optional<double> quality)
{
- if (!m_originClean) {
- ec = SECURITY_ERR;
- return String();
- }
+ if (!m_originClean)
+ return Exception { SECURITY_ERR };
if (m_size.isEmpty() || !buffer())
- return String("data:,");
+ return String { ASCIILiteral { "data:," } };
- String encodingMimeType = toEncodingMimeType(mimeType);
+ String encodingMIMEType = toEncodingMimeType(mimeType);
#if USE(CG)
// Try to get ImageData first, as that may avoid lossy conversions.
- RefPtr<ImageData> imageData = getImageData();
-
- if (imageData)
- return ImageDataToDataURL(*imageData, encodingMimeType, quality);
+ if (auto imageData = getImageData())
+ return dataURL(*imageData, encodingMIMEType, quality);
#endif
makeRenderingResultsAvailable();
- return buffer()->toDataURL(encodingMimeType, quality);
+ return buffer()->toDataURL(encodingMIMEType, quality);
}
-PassRefPtr<ImageData> HTMLCanvasElement::getImageData()
+RefPtr<ImageData> HTMLCanvasElement::getImageData()
{
- if (!m_context || !m_context->is3d())
- return 0;
+#if ENABLE(WEBGL)
+ if (!is3D())
+ return nullptr;
-#if ENABLE(WEBGL)
- WebGLRenderingContext* ctx = static_cast<WebGLRenderingContext*>(m_context.get());
+ WebGLRenderingContextBase* ctx = static_cast<WebGLRenderingContextBase*>(m_context.get());
return ctx->paintRenderingResultsToImageData();
#else
- return 0;
+ return nullptr;
#endif
}
FloatRect HTMLCanvasElement::convertLogicalToDevice(const FloatRect& logicalRect) const
{
FloatRect deviceRect(logicalRect);
- deviceRect.scale(m_deviceScaleFactor);
float x = floorf(deviceRect.x());
float y = floorf(deviceRect.y());
@@ -536,38 +517,44 @@ FloatRect HTMLCanvasElement::convertLogicalToDevice(const FloatRect& logicalRect
FloatSize HTMLCanvasElement::convertLogicalToDevice(const FloatSize& logicalSize) const
{
- float width = ceilf(logicalSize.width() * m_deviceScaleFactor);
- float height = ceilf(logicalSize.height() * m_deviceScaleFactor);
+ float width = ceilf(logicalSize.width());
+ float height = ceilf(logicalSize.height());
return FloatSize(width, height);
}
FloatSize HTMLCanvasElement::convertDeviceToLogical(const FloatSize& deviceSize) const
{
- float width = ceilf(deviceSize.width() / m_deviceScaleFactor);
- float height = ceilf(deviceSize.height() / m_deviceScaleFactor);
+ float width = ceilf(deviceSize.width());
+ float height = ceilf(deviceSize.height());
return FloatSize(width, height);
}
SecurityOrigin* HTMLCanvasElement::securityOrigin() const
{
- return document().securityOrigin();
+ return &document().securityOrigin();
}
bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
{
+ auto& settings = document().settings();
+
+ auto area = size.area<RecordOverflow>();
+ if (area.hasOverflowed())
+ return false;
+
+ if (area > settings.maximumAccelerated2dCanvasSize())
+ return false;
+
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
- UNUSED_PARAM(size);
- return document().settings() && document().settings()->canvasUsesAcceleratedDrawing();
+ return settings.canvasUsesAcceleratedDrawing();
#elif ENABLE(ACCELERATED_2D_CANVAS)
if (m_context && !m_context->is2d())
return false;
- Settings* settings = document().settings();
- if (!settings || !settings->accelerated2dCanvasEnabled())
+ if (!settings.accelerated2dCanvasEnabled())
return false;
- // Do not use acceleration for small canvas.
- if (size.width() * size.height() < settings->minimumAccelerated2dCanvasSize())
+ if (area < settings.minimumAccelerated2dCanvasSize())
return false;
return true;
@@ -577,6 +564,58 @@ bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
#endif
}
+size_t HTMLCanvasElement::memoryCost() const
+{
+ if (!m_imageBuffer)
+ return 0;
+ return m_imageBuffer->memoryCost();
+}
+
+size_t HTMLCanvasElement::externalMemoryCost() const
+{
+ if (!m_imageBuffer)
+ return 0;
+ return m_imageBuffer->externalMemoryCost();
+}
+
+void HTMLCanvasElement::setUsesDisplayListDrawing(bool usesDisplayListDrawing)
+{
+ if (usesDisplayListDrawing == m_usesDisplayListDrawing)
+ return;
+
+ m_usesDisplayListDrawing = usesDisplayListDrawing;
+
+ if (is<CanvasRenderingContext2D>(m_context.get()))
+ downcast<CanvasRenderingContext2D>(*m_context).setUsesDisplayListDrawing(m_usesDisplayListDrawing);
+}
+
+void HTMLCanvasElement::setTracksDisplayListReplay(bool tracksDisplayListReplay)
+{
+ if (tracksDisplayListReplay == m_tracksDisplayListReplay)
+ return;
+
+ m_tracksDisplayListReplay = tracksDisplayListReplay;
+
+ if (is<CanvasRenderingContext2D>(m_context.get()))
+ downcast<CanvasRenderingContext2D>(*m_context).setTracksDisplayListReplay(m_tracksDisplayListReplay);
+}
+
+String HTMLCanvasElement::displayListAsText(DisplayList::AsTextFlags flags) const
+{
+ if (is<CanvasRenderingContext2D>(m_context.get()))
+ return downcast<CanvasRenderingContext2D>(*m_context).displayListAsText(flags);
+
+ return String();
+}
+
+String HTMLCanvasElement::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
+{
+ if (is<CanvasRenderingContext2D>(m_context.get()))
+ return downcast<CanvasRenderingContext2D>(*m_context).replayDisplayListAsText(flags);
+
+ return String();
+}
+
void HTMLCanvasElement::createImageBuffer() const
{
ASSERT(!m_imageBuffer);
@@ -589,43 +628,62 @@ void HTMLCanvasElement::createImageBuffer() const
if (!deviceSize.isExpressibleAsIntSize())
return;
-#if PLATFORM(IOS)
- if (deviceSize.width() * deviceSize.height() * 4 > m_maximumDecodedImageSize)
+ if (deviceSize.width() * deviceSize.height() > maxCanvasArea) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("Canvas area exceeds the maximum limit (width * height > ");
+ stringBuilder.appendNumber(maxCanvasArea);
+ stringBuilder.appendLiteral(").");
+ document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
return;
-#else
- if (deviceSize.width() * deviceSize.height() > MaxCanvasArea)
+ }
+
+ // Make sure we don't use more pixel memory than the system can support.
+ size_t requestedPixelMemory = 4 * width() * height();
+ if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
+ stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
+ stringBuilder.appendLiteral(" MB).");
+ document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
return;
-#endif
+ }
IntSize bufferSize(deviceSize.width(), deviceSize.height());
if (!bufferSize.width() || !bufferSize.height())
return;
RenderingMode renderingMode = shouldAccelerate(bufferSize) ? Accelerated : Unaccelerated;
- m_imageBuffer = ImageBuffer::create(size(), m_deviceScaleFactor, ColorSpaceDeviceRGB, renderingMode);
+
+ setImageBuffer(ImageBuffer::create(size(), renderingMode));
if (!m_imageBuffer)
return;
- m_imageBuffer->context()->setShadowsIgnoreTransforms(true);
- m_imageBuffer->context()->setImageInterpolationQuality(DefaultInterpolationQuality);
- if (document().settings() && !document().settings()->antialiased2dCanvasEnabled())
- m_imageBuffer->context()->setShouldAntialias(false);
- m_imageBuffer->context()->setStrokeThickness(1);
- m_contextStateSaver = adoptPtr(new GraphicsContextStateSaver(*m_imageBuffer->context()));
+ m_imageBuffer->context().setShadowsIgnoreTransforms(true);
+ m_imageBuffer->context().setImageInterpolationQuality(defaultInterpolationQuality);
+ m_imageBuffer->context().setStrokeThickness(1);
+ m_contextStateSaver = std::make_unique<GraphicsContextStateSaver>(m_imageBuffer->context());
JSC::JSLockHolder lock(scriptExecutionContext()->vm());
- size_t numBytes = 4 * m_imageBuffer->internalSize().width() * m_imageBuffer->internalSize().height();
- scriptExecutionContext()->vm()->heap.reportExtraMemoryCost(numBytes);
+ scriptExecutionContext()->vm().heap.reportExtraMemoryAllocated(memoryCost());
-#if USE(IOSURFACE_CANVAS_BACKING_STORE) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING))
+#if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
if (m_context && m_context->is2d())
// Recalculate compositing requirements if acceleration state changed.
- const_cast<HTMLCanvasElement*>(this)->setNeedsStyleRecalc(SyntheticStyleChange);
+ const_cast<HTMLCanvasElement*>(this)->invalidateStyleAndLayerComposition();
#endif
}
+void HTMLCanvasElement::setImageBuffer(std::unique_ptr<ImageBuffer> buffer) const
+{
+ removeFromActivePixelMemory(memoryCost());
+
+ m_imageBuffer = WTFMove(buffer);
+
+ activePixelMemory += memoryCost();
+}
+
GraphicsContext* HTMLCanvasElement::drawingContext() const
{
- return buffer() ? m_imageBuffer->context() : nullptr;
+ return buffer() ? &m_imageBuffer->context() : nullptr;
}
GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
@@ -661,16 +719,15 @@ void HTMLCanvasElement::clearImageBuffer() const
m_didClearImageBuffer = true;
- if (m_context->is2d()) {
- CanvasRenderingContext2D* context2D = static_cast<CanvasRenderingContext2D*>(m_context.get());
+ if (is<CanvasRenderingContext2D>(*m_context)) {
// No need to undo transforms/clip/etc. because we are called right after the context is reset.
- context2D->clearRect(0, 0, width(), height());
+ downcast<CanvasRenderingContext2D>(*m_context).clearRect(0, 0, width(), height());
}
}
void HTMLCanvasElement::clearCopiedImage()
{
- m_copiedImage.clear();
+ m_copiedImage = nullptr;
m_didClearImageBuffer = false;
}