Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLCanvasElement.cpp
index ae520f3..4d6c77e 100644 (file)
 #include "config.h"
 #include "core/html/HTMLCanvasElement.h"
 
-#include <math.h>
-#include "HTMLNames.h"
-#include "RuntimeEnabledFeatures.h"
 #include "bindings/v8/ExceptionMessages.h"
 #include "bindings/v8/ExceptionState.h"
 #include "bindings/v8/ScriptController.h"
+#include "core/HTMLNames.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/frame/LocalFrame.h"
+#include "core/frame/Settings.h"
 #include "core/html/ImageData.h"
 #include "core/html/canvas/Canvas2DContextAttributes.h"
 #include "core/html/canvas/CanvasRenderingContext2D.h"
 #include "core/html/canvas/WebGLContextAttributes.h"
+#include "core/html/canvas/WebGLContextEvent.h"
 #include "core/html/canvas/WebGLRenderingContext.h"
-#include "core/frame/Frame.h"
-#include "core/frame/Settings.h"
 #include "core/rendering/RenderHTMLCanvas.h"
 #include "platform/MIMETypeRegistry.h"
+#include "platform/RuntimeEnabledFeatures.h"
 #include "platform/graphics/Canvas2DImageBufferSurface.h"
 #include "platform/graphics/GraphicsContextStateSaver.h"
 #include "platform/graphics/ImageBuffer.h"
@@ -52,6 +52,8 @@
 #include "platform/graphics/gpu/WebGLImageBufferSurface.h"
 #include "platform/transforms/AffineTransform.h"
 #include "public/platform/Platform.h"
+#include <math.h>
+#include <v8.h>
 
 namespace WebCore {
 
@@ -69,10 +71,12 @@ static const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pix
 //In Skia, we will also limit width/height to 32767.
 static const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels.
 
-HTMLCanvasElement::HTMLCanvasElement(Document& document)
+DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver);
+
+inline HTMLCanvasElement::HTMLCanvasElement(Document& document)
     : HTMLElement(canvasTag, document)
+    , DocumentVisibilityObserver(document)
     , m_size(DefaultWidth, DefaultHeight)
-    , m_rendererIsCanvas(false)
     , m_ignoreReset(false)
     , m_accelerationDisabled(false)
     , m_externallyAllocatedMemory(0)
@@ -83,19 +87,19 @@ HTMLCanvasElement::HTMLCanvasElement(Document& document)
     ScriptWrappable::init(this);
 }
 
-PassRefPtr<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
-{
-    return adoptRef(new HTMLCanvasElement(document));
-}
+DEFINE_NODE_FACTORY(HTMLCanvasElement)
 
 HTMLCanvasElement::~HTMLCanvasElement()
 {
-    setExternallyAllocatedMemory(0);
-    HashSet<CanvasObserver*>::iterator end = m_observers.end();
-    for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it)
+    v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory);
+#if !ENABLE(OILPAN)
+    HashSet<RawPtr<CanvasObserver> >::iterator end = m_observers.end();
+    for (HashSet<RawPtr<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it)
         (*it)->canvasDestroyed(this);
-
-    m_context.clear(); // Ensure this goes away before the ImageBuffer.
+    // Ensure these go away before the ImageBuffer.
+    m_contextStateSaver.clear();
+    m_context.clear();
+#endif
 }
 
 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
@@ -107,13 +111,9 @@ void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicSt
 
 RenderObject* HTMLCanvasElement::createRenderer(RenderStyle* style)
 {
-    Frame* frame = document().frame();
-    if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript)) {
-        m_rendererIsCanvas = true;
+    LocalFrame* frame = document().frame();
+    if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
         return new RenderHTMLCanvas(this);
-    }
-
-    m_rendererIsCanvas = false;
     return HTMLElement::createRenderer(style);
 }
 
@@ -167,38 +167,33 @@ CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, Canvas
         if (!m_context) {
             blink::Platform::current()->histogramEnumeration("Canvas.ContextType", Context2d, ContextTypeCount);
             m_context = CanvasRenderingContext2D::create(this, static_cast<Canvas2DContextAttributes*>(attrs), document().inQuirksMode());
-            if (m_context)
-                scheduleLayerUpdate();
+            setNeedsCompositingUpdate();
         }
         return m_context.get();
     }
 
-    Settings* settings = document().settings();
-    if (settings && settings->webGLEnabled()) {
-        // Accept the legacy "webkit-3d" name as well as the provisional "experimental-webgl" name.
-        // Now that WebGL is ratified, we will also accept "webgl" as the context name in Chrome.
-        ContextType contextType;
-        bool is3dContext = true;
-        if (type == "webkit-3d")
-            contextType = ContextWebkit3d;
-        else if (type == "experimental-webgl")
-            contextType = ContextExperimentalWebgl;
-        else if (type == "webgl")
-            contextType = ContextWebgl;
-        else
-            is3dContext = false;
-
-        if (is3dContext) {
-            if (m_context && !m_context->is3d())
-                return 0;
-            if (!m_context) {
-                blink::Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, ContextTypeCount);
-                m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs));
-                if (m_context)
-                    scheduleLayerUpdate();
-            }
-            return m_context.get();
+    // Accept the the provisional "experimental-webgl" or official "webgl" context ID.
+    ContextType contextType;
+    bool is3dContext = true;
+    if (type == "experimental-webgl")
+        contextType = ContextExperimentalWebgl;
+    else if (type == "webgl")
+        contextType = ContextWebgl;
+    else
+        is3dContext = false;
+
+    if (is3dContext) {
+        if (m_context && !m_context->is3d()) {
+            dispatchEvent(WebGLContextEvent::create(EventTypeNames::webglcontextcreationerror, false, true, "Canvas has an existing, non-WebGL context"));
+            return 0;
         }
+        if (!m_context) {
+            blink::Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, ContextTypeCount);
+            m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs));
+            setNeedsCompositingUpdate();
+            updateExternallyAllocatedMemory();
+        }
+        return m_context.get();
     }
     return 0;
 }
@@ -215,7 +210,7 @@ void HTMLCanvasElement::didDraw(const FloatRect& rect)
             return;
 
         m_dirtyRect.unite(r);
-        ro->repaintRectangle(enclosingIntRect(m_dirtyRect));
+        ro->invalidatePaintRectangle(enclosingIntRect(m_dirtyRect));
     }
 
     notifyObserversCanvasChanged(rect);
@@ -223,8 +218,8 @@ void HTMLCanvasElement::didDraw(const FloatRect& rect)
 
 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect)
 {
-    HashSet<CanvasObserver*>::iterator end = m_observers.end();
-    for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it)
+    WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator end = m_observers.end();
+    for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it)
         (*it)->canvasChanged(this, rect);
 }
 
@@ -270,19 +265,19 @@ void HTMLCanvasElement::reset()
         toWebGLRenderingContext(m_context.get())->reshape(width(), height());
 
     if (RenderObject* renderer = this->renderer()) {
-        if (m_rendererIsCanvas) {
+        if (renderer->isCanvas()) {
             if (oldSize != size()) {
                 toRenderHTMLCanvas(renderer)->canvasSizeChanged();
                 if (renderBox() && renderBox()->hasAcceleratedCompositing())
                     renderBox()->contentChanged(CanvasChanged);
             }
             if (hadImageBuffer)
-                renderer->repaint();
+                renderer->paintInvalidationForWholeRenderer();
         }
     }
 
-    HashSet<CanvasObserver*>::iterator end = m_observers.end();
-    for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it)
+    WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator end = m_observers.end();
+    for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it)
         (*it)->canvasResized(this);
 }
 
@@ -300,7 +295,7 @@ bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
 }
 
 
-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();
@@ -319,10 +314,14 @@ void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r, boo
         if (imageBuffer) {
             CompositeOperator compositeOperator = !m_context || m_context->hasAlpha() ? CompositeSourceOver : CompositeCopy;
             if (m_presentedImage)
-                context->drawImage(m_presentedImage.get(), pixelSnappedIntRect(r), compositeOperator, DoNotRespectImageOrientation, useLowQualityScale);
+                context->drawImage(m_presentedImage.get(), pixelSnappedIntRect(r), compositeOperator, DoNotRespectImageOrientation);
             else
-                context->drawImageBuffer(imageBuffer, pixelSnappedIntRect(r), compositeOperator, blink::WebBlendModeNormal, useLowQualityScale);
+                context->drawImageBuffer(imageBuffer, pixelSnappedIntRect(r), 0, compositeOperator);
         }
+    } else {
+        // When alpha is false, we should draw to opaque black.
+        if (m_context && !m_context->hasAlpha())
+            context->fillRect(FloatRect(r), Color(0, 0, 0));
     }
 
     if (is3D())
@@ -339,22 +338,28 @@ void HTMLCanvasElement::makePresentationCopy()
     if (!m_presentedImage) {
         // The buffer contains the last presented data, so save a copy of it.
         m_presentedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
+        updateExternallyAllocatedMemory();
     }
 }
 
 void HTMLCanvasElement::clearPresentationCopy()
 {
     m_presentedImage.clear();
+    updateExternallyAllocatedMemory();
 }
 
 void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
 {
     m_size = size;
     m_didFailToCreateImageBuffer = false;
-    m_contextStateSaver.clear();
-    m_imageBuffer.clear();
-    setExternallyAllocatedMemory(0);
+    discardImageBuffer();
     clearCopiedImage();
+    if (m_context && m_context->is2d()) {
+        CanvasRenderingContext2D* context2d = toCanvasRenderingContext2D(m_context.get());
+        if (context2d->isContextLost()) {
+            context2d->restoreContext();
+        }
+    }
 }
 
 String HTMLCanvasElement::toEncodingMimeType(const String& mimeType)
@@ -368,34 +373,47 @@ String HTMLCanvasElement::toEncodingMimeType(const String& mimeType)
     return lowercaseMimeType;
 }
 
-String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionState& exceptionState)
+const AtomicString HTMLCanvasElement::imageSourceURL() const
 {
-    if (!m_originClean) {
-        exceptionState.throwSecurityError("Tainted canvases may not be exported.");
-        return String();
-    }
+    return AtomicString(toDataURLInternal("image/png", 0, true));
+}
 
+String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double* quality, bool isSaving) const
+{
     if (m_size.isEmpty() || !buffer())
         return String("data:,");
 
     String encodingMimeType = toEncodingMimeType(mimeType);
 
     // Try to get ImageData first, as that may avoid lossy conversions.
-    RefPtr<ImageData> imageData = getImageData();
+    RefPtrWillBeRawPtr<ImageData> imageData = getImageData();
 
     if (imageData)
         return ImageDataToDataURL(ImageDataBuffer(imageData->size(), imageData->data()), encodingMimeType, quality);
 
-    if (m_context)
+    if (m_context && m_context->is3d()) {
+        toWebGLRenderingContext(m_context.get())->setSavingImage(isSaving);
         m_context->paintRenderingResultsToCanvas();
+        toWebGLRenderingContext(m_context.get())->setSavingImage(false);
+    }
 
     return buffer()->toDataURL(encodingMimeType, quality);
 }
 
-PassRefPtr<ImageData> HTMLCanvasElement::getImageData()
+String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionState& exceptionState) const
+{
+    if (!m_originClean) {
+        exceptionState.throwSecurityError("Tainted canvases may not be exported.");
+        return String();
+    }
+
+    return toDataURLInternal(mimeType, quality);
+}
+
+PassRefPtrWillBeRawPtr<ImageData> HTMLCanvasElement::getImageData() const
 {
     if (!m_context || !m_context->is3d())
-       return 0;
+        return nullptr;
     return toWebGLRenderingContext(m_context.get())->paintRenderingResultsToImageData();
 }
 
@@ -447,7 +465,15 @@ PassOwnPtr<ImageBufferSurface> HTMLCanvasElement::createImageBufferSurface(const
 
 void HTMLCanvasElement::createImageBuffer()
 {
+    createImageBufferInternal();
+    if (m_didFailToCreateImageBuffer && m_context && m_context->is2d())
+        toCanvasRenderingContext2D(m_context.get())->loseContext();
+}
+
+void HTMLCanvasElement::createImageBufferInternal()
+{
     ASSERT(!m_imageBuffer);
+    ASSERT(!m_contextStateSaver);
 
     m_didFailToCreateImageBuffer = true;
     m_didClearImageBuffer = true;
@@ -466,20 +492,23 @@ void HTMLCanvasElement::createImageBuffer()
     OwnPtr<ImageBufferSurface> surface = createImageBufferSurface(deviceSize, &msaaSampleCount);
     if (!surface->isValid())
         return;
+
     m_imageBuffer = ImageBuffer::create(surface.release());
+    m_imageBuffer->setClient(this);
 
     m_didFailToCreateImageBuffer = false;
 
-    setExternallyAllocatedMemory(4 * width() * height());
+    updateExternallyAllocatedMemory();
 
     if (is3D()) {
         // Early out for WebGL canvases
-        m_contextStateSaver.clear();
         return;
     }
 
+    m_imageBuffer->setClient(this);
     m_imageBuffer->context()->setShouldClampToSourceRect(false);
-    m_imageBuffer->context()->setImageInterpolationQuality(DefaultInterpolationQuality);
+    m_imageBuffer->context()->disableAntialiasingOptimizationForHairlineImages();
+    m_imageBuffer->context()->setImageInterpolationQuality(CanvasDefaultInterpolationQuality);
     // Enabling MSAA overrides a request to disable antialiasing. This is true regardless of whether the
     // rendering mode is accelerated or not. For consistency, we don't want to apply AA in accelerated
     // canvases but not in unaccelerated canvases.
@@ -489,15 +518,51 @@ void HTMLCanvasElement::createImageBuffer()
     // See CanvasRenderingContext2D::State::State() for more information.
     m_imageBuffer->context()->setMiterLimit(10);
     m_imageBuffer->context()->setStrokeThickness(1);
+#if ASSERT_ENABLED
+    m_imageBuffer->context()->disableDestructionChecks(); // 2D canvas is allowed to leave context in an unfinalized state.
+#endif
     m_contextStateSaver = adoptPtr(new GraphicsContextStateSaver(*m_imageBuffer->context()));
 
-    // Recalculate compositing requirements if acceleration state changed.
     if (m_context)
-        scheduleLayerUpdate();
+        setNeedsCompositingUpdate();
+}
+
+void HTMLCanvasElement::notifySurfaceInvalid()
+{
+    if (m_context && m_context->is2d()) {
+        CanvasRenderingContext2D* context2d = toCanvasRenderingContext2D(m_context.get());
+        context2d->loseContext();
+    }
+}
+
+void HTMLCanvasElement::trace(Visitor* visitor)
+{
+    visitor->trace(m_observers);
+    visitor->trace(m_context);
+    DocumentVisibilityObserver::trace(visitor);
+    HTMLElement::trace(visitor);
 }
 
-void HTMLCanvasElement::setExternallyAllocatedMemory(intptr_t externallyAllocatedMemory)
+void HTMLCanvasElement::updateExternallyAllocatedMemory() const
 {
+    int bufferCount = 0;
+    if (m_imageBuffer)
+        bufferCount++;
+    if (is3D())
+        bufferCount += 2;
+    if (m_copiedImage)
+        bufferCount++;
+    if (m_presentedImage)
+        bufferCount++;
+
+    Checked<intptr_t, RecordOverflow> checkedExternallyAllocatedMemory = 4 * bufferCount;
+    checkedExternallyAllocatedMemory *= width();
+    checkedExternallyAllocatedMemory *= height();
+    intptr_t externallyAllocatedMemory;
+    if (checkedExternallyAllocatedMemory.safeGet(externallyAllocatedMemory) == CheckedState::DidOverflow)
+        externallyAllocatedMemory = std::numeric_limits<intptr_t>::max();
+
+    // Subtracting two intptr_t that are known to be positive will never underflow.
     v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(externallyAllocatedMemory - m_externallyAllocatedMemory);
     m_externallyAllocatedMemory = externallyAllocatedMemory;
 }
@@ -509,10 +574,8 @@ GraphicsContext* HTMLCanvasElement::drawingContext() const
 
 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
 {
-    if (m_didFailToCreateImageBuffer) {
-        ASSERT(!hasImageBuffer());
+    if (!hasImageBuffer())
         return 0;
-    }
 
     return drawingContext();
 }
@@ -528,7 +591,7 @@ void HTMLCanvasElement::ensureUnacceleratedImageBuffer()
 {
     if ((hasImageBuffer() && !m_imageBuffer->isAccelerated()) || m_didFailToCreateImageBuffer)
         return;
-    m_imageBuffer.clear();
+    discardImageBuffer();
     OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque;
     m_imageBuffer = ImageBuffer::create(size(), opacityMode);
     m_didFailToCreateImageBuffer = !m_imageBuffer;
@@ -540,6 +603,7 @@ Image* HTMLCanvasElement::copiedImage() const
         if (m_context)
             m_context->paintRenderingResultsToCanvas();
         m_copiedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
+        updateExternallyAllocatedMemory();
     }
     return m_copiedImage.get();
 }
@@ -559,10 +623,23 @@ void HTMLCanvasElement::clearImageBuffer()
     }
 }
 
+void HTMLCanvasElement::discardImageBuffer()
+{
+    m_contextStateSaver.clear(); // uses context owned by m_imageBuffer
+    m_imageBuffer.clear();
+    updateExternallyAllocatedMemory();
+}
+
+bool HTMLCanvasElement::hasValidImageBuffer() const
+{
+    return m_imageBuffer && m_imageBuffer->isSurfaceValid();
+}
+
 void HTMLCanvasElement::clearCopiedImage()
 {
     m_copiedImage.clear();
     m_didClearImageBuffer = false;
+    updateExternallyAllocatedMemory();
 }
 
 AffineTransform HTMLCanvasElement::baseTransform() const
@@ -571,4 +648,62 @@ AffineTransform HTMLCanvasElement::baseTransform() const
     return m_imageBuffer->baseTransform();
 }
 
+void HTMLCanvasElement::didChangeVisibilityState(PageVisibilityState visibility)
+{
+    if (hasImageBuffer()) {
+        bool hidden = visibility != PageVisibilityStateVisible;
+        if (hidden) {
+            clearCopiedImage();
+            if (is3D()) {
+                discardImageBuffer();
+            }
+        }
+        if (hasImageBuffer()) {
+            m_imageBuffer->setIsHidden(hidden);
+        }
+    }
+}
+
+void HTMLCanvasElement::didMoveToNewDocument(Document& oldDocument)
+{
+    setObservedDocument(document());
+    HTMLElement::didMoveToNewDocument(oldDocument);
+}
+
+PassRefPtr<Image> HTMLCanvasElement::getSourceImageForCanvas(SourceImageMode mode, SourceImageStatus* status) const
+{
+    if (!width() || !height()) {
+        *status = ZeroSizeCanvasSourceImageStatus;
+        return nullptr;
+    }
+
+    if (!buffer()) {
+        *status = InvalidSourceImageStatus;
+        return nullptr;
+    }
+
+    if (mode == CopySourceImageIfVolatile) {
+        *status = NormalSourceImageStatus;
+        return copiedImage();
+    }
+
+    if (m_context && m_context->is3d()) {
+        m_context->paintRenderingResultsToCanvas();
+        *status = ExternalSourceImageStatus;
+    } else {
+        *status = NormalSourceImageStatus;
+    }
+    return m_imageBuffer->copyImage(DontCopyBackingStore, Unscaled);
+}
+
+bool HTMLCanvasElement::wouldTaintOrigin(SecurityOrigin*) const
+{
+    return !originClean();
+}
+
+FloatSize HTMLCanvasElement::sourceSize() const
+{
+    return FloatSize(width(), height());
+}
+
 }