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/html/HTMLCanvasElement.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/html/HTMLCanvasElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLCanvasElement.cpp | 567 |
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; } |