Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLCanvasElement.cpp
index 8c4fd5d..bb00e4d 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 "bindings/core/v8/ExceptionMessages.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ScriptController.h"
+#include "core/HTMLNames.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/frame/LocalFrame.h"
 #include "core/html/canvas/WebGLContextEvent.h"
 #include "core/html/canvas/WebGLRenderingContext.h"
 #include "core/rendering/RenderHTMLCanvas.h"
+#include "core/rendering/RenderLayer.h"
 #include "platform/MIMETypeRegistry.h"
+#include "platform/RuntimeEnabledFeatures.h"
 #include "platform/graphics/Canvas2DImageBufferSurface.h"
 #include "platform/graphics/GraphicsContextStateSaver.h"
 #include "platform/graphics/ImageBuffer.h"
+#include "platform/graphics/RecordingImageBufferSurface.h"
+#include "platform/graphics/StaticBitmapImage.h"
 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
 #include "platform/graphics/gpu/WebGLImageBufferSurface.h"
 #include "platform/transforms/AffineTransform.h"
 #include "public/platform/Platform.h"
+#include <math.h>
+#include <v8.h>
 
-namespace WebCore {
+namespace blink {
 
 using namespace HTMLNames;
 
@@ -70,11 +74,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)
@@ -82,22 +87,21 @@ HTMLCanvasElement::HTMLCanvasElement(Document& document)
     , m_didFailToCreateImageBuffer(false)
     , m_didClearImageBuffer(false)
 {
-    ScriptWrappable::init(this);
 }
 
-PassRefPtr<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
-{
-    return adoptRef(new HTMLCanvasElement(document));
-}
+DEFINE_NODE_FACTORY(HTMLCanvasElement)
 
 HTMLCanvasElement::~HTMLCanvasElement()
 {
+    resetDirtyRect();
     v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory);
-    HashSet<CanvasObserver*>::iterator end = m_observers.end();
-    for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it)
-        (*it)->canvasDestroyed(this);
-
-    m_context.clear(); // Ensure this goes away before the ImageBuffer.
+#if !ENABLE(OILPAN)
+    for (CanvasObserver* canvasObserver : m_observers)
+        canvasObserver->canvasDestroyed(this);
+    // Ensure these go away before the ImageBuffer.
+    m_contextStateSaver.clear();
+    m_context.clear();
+#endif
 }
 
 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
@@ -110,12 +114,8 @@ void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicSt
 RenderObject* HTMLCanvasElement::createRenderer(RenderStyle* style)
 {
     LocalFrame* frame = document().frame();
-    if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript)) {
-        m_rendererIsCanvas = true;
+    if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
         return new RenderHTMLCanvas(this);
-    }
-
-    m_rendererIsCanvas = false;
     return HTMLElement::createRenderer(style);
 }
 
@@ -152,11 +152,10 @@ CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, Canvas
     // 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.
     enum ContextType {
-        Context2d,
-        ContextWebkit3d,
-        ContextExperimentalWebgl,
-        ContextWebgl,
-        // Only add new items to the end and keep the order of existing items.
+        // Do not change assigned numbers of existing items: add new features to the end of the list.
+        Context2d = 0,
+        ContextExperimentalWebgl = 2,
+        ContextWebgl = 3,
         ContextTypeCount,
     };
 
@@ -165,70 +164,95 @@ CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, Canvas
     // once it is created.
     if (type == "2d") {
         if (m_context && !m_context->is2d())
-            return 0;
+            return nullptr;
         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();
+
+            m_context = CanvasRenderingContext2D::create(this, static_cast<Canvas2DContextAttributes*>(attrs), document());
+            setNeedsCompositingUpdate();
         }
         return m_context.get();
     }
 
-    // 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()) {
-            dispatchEvent(WebGLContextEvent::create(EventTypeNames::webglcontextcreationerror, false, true, "Canvas has an existing, non-WebGL context"));
-            return 0;
-        }
+    // Accept the the provisional "experimental-webgl" or official "webgl" context ID.
+    if (type == "webgl" || type == "experimental-webgl") {
+        ContextType contextType = (type == "webgl") ? ContextWebgl : ContextExperimentalWebgl;
         if (!m_context) {
             blink::Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, ContextTypeCount);
             m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs));
-            if (m_context) {
-                scheduleLayerUpdate();
-                updateExternallyAllocatedMemory();
-            }
+            setNeedsCompositingUpdate();
+            updateExternallyAllocatedMemory();
+        } else if (!m_context->is3d()) {
+            dispatchEvent(WebGLContextEvent::create(EventTypeNames::webglcontextcreationerror, false, true, "Canvas has an existing, non-WebGL context"));
+            return nullptr;
         }
         return m_context.get();
     }
-    return 0;
+
+    return nullptr;
 }
 
 void HTMLCanvasElement::didDraw(const FloatRect& rect)
 {
+    if (rect.isEmpty())
+        return;
     clearCopiedImage();
+    if (m_dirtyRect.isEmpty())
+        blink::Platform::current()->currentThread()->addTaskObserver(this);
+    m_dirtyRect.unite(rect);
+}
 
+void HTMLCanvasElement::didFinalizeFrame()
+{
+    if (m_dirtyRect.isEmpty())
+        return;
+
+    // Propagate the m_dirtyRect accumulated so far to the compositor
+    // before restarting with a blank dirty rect.
+    FloatRect srcRect(0, 0, size().width(), size().height());
+    m_dirtyRect.intersect(srcRect);
     if (RenderBox* ro = renderBox()) {
-        FloatRect destRect = ro->contentBoxRect();
-        FloatRect r = mapRect(rect, FloatRect(0, 0, size().width(), size().height()), destRect);
-        r.intersect(destRect);
-        if (r.isEmpty() || m_dirtyRect.contains(r))
-            return;
+        FloatRect mappedDirtyRect = mapRect(m_dirtyRect, srcRect, ro->contentBoxRect());
+        // For querying RenderLayer::compositingState()
+        // FIXME: is this invalidation using the correct compositing state?
+        DisableCompositingQueryAsserts disabler;
+        ro->invalidatePaintRectangle(enclosingIntRect(mappedDirtyRect));
+    }
+    notifyObserversCanvasChanged(m_dirtyRect);
+    blink::Platform::current()->currentThread()->removeTaskObserver(this);
+    m_dirtyRect = FloatRect();
+}
+
+void HTMLCanvasElement::resetDirtyRect()
+{
+    if (m_dirtyRect.isEmpty())
+        return;
+    blink::Platform::current()->currentThread()->removeTaskObserver(this);
+    m_dirtyRect = FloatRect();
+}
 
-        m_dirtyRect.unite(r);
-        ro->repaintRectangle(enclosingIntRect(m_dirtyRect));
+void HTMLCanvasElement::didProcessTask()
+{
+    // This method gets invoked if didDraw was called earlier in the current task.
+    ASSERT(!m_dirtyRect.isEmpty());
+    if (is3D()) {
+        didFinalizeFrame();
+    } else {
+        ASSERT(hasImageBuffer());
+        m_imageBuffer->finalizeFrame(m_dirtyRect);
     }
+    ASSERT(m_dirtyRect.isEmpty());
+}
 
-    notifyObserversCanvasChanged(rect);
+void HTMLCanvasElement::willProcessTask()
+{
+    ASSERT_NOT_REACHED();
 }
 
 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect)
 {
-    HashSet<CanvasObserver*>::iterator end = m_observers.end();
-    for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it)
-        (*it)->canvasChanged(this, rect);
+    for (CanvasObserver* canvasObserver : m_observers)
+        canvasObserver->canvasChanged(this, rect);
 }
 
 void HTMLCanvasElement::reset()
@@ -236,6 +260,8 @@ void HTMLCanvasElement::reset()
     if (m_ignoreReset)
         return;
 
+    resetDirtyRect();
+
     bool ok;
     bool hadImageBuffer = hasImageBuffer();
 
@@ -273,20 +299,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->setShouldDoFullPaintInvalidation();
         }
     }
 
-    HashSet<CanvasObserver*>::iterator end = m_observers.end();
-    for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it)
-        (*it)->canvasResized(this);
+    for (CanvasObserver* canvasObserver : m_observers)
+        canvasObserver->canvasResized(this);
 }
 
 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
@@ -305,12 +330,6 @@ bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
 
 void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r)
 {
-    // Clear the dirty rect
-    m_dirtyRect = FloatRect();
-
-    if (context->paintingDisabled())
-        return;
-
     if (m_context) {
         if (!paintsIntoCanvasBuffer() && !document().printing())
             return;
@@ -324,8 +343,12 @@ void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r)
             if (m_presentedImage)
                 context->drawImage(m_presentedImage.get(), pixelSnappedIntRect(r), compositeOperator, DoNotRespectImageOrientation);
             else
-                context->drawImageBuffer(imageBuffer, pixelSnappedIntRect(r), compositeOperator, blink::WebBlendModeNormal);
+                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())
@@ -358,6 +381,12 @@ void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
     m_didFailToCreateImageBuffer = false;
     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)
@@ -371,31 +400,44 @@ 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 nullptr;
@@ -429,27 +471,99 @@ bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
     return true;
 }
 
+class UnacceleratedSurfaceFactory : public RecordingImageBufferFallbackSurfaceFactory {
+public:
+    virtual PassOwnPtr<ImageBufferSurface> createSurface(const IntSize& size, OpacityMode opacityMode)
+    {
+        return adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
+    }
+
+    virtual ~UnacceleratedSurfaceFactory() { }
+};
+
+class Accelerated2dSurfaceFactory : public RecordingImageBufferFallbackSurfaceFactory {
+public:
+    Accelerated2dSurfaceFactory(int msaaSampleCount) : m_msaaSampleCount(msaaSampleCount) { }
+
+    virtual PassOwnPtr<ImageBufferSurface> createSurface(const IntSize& size, OpacityMode opacityMode)
+    {
+        OwnPtr<ImageBufferSurface> surface = adoptPtr(new Canvas2DImageBufferSurface(size, opacityMode, m_msaaSampleCount));
+        if (surface->isValid())
+            return surface.release();
+        return adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
+    }
+
+    virtual ~Accelerated2dSurfaceFactory() { }
+private:
+    int m_msaaSampleCount;
+};
+
+PassOwnPtr<RecordingImageBufferFallbackSurfaceFactory> HTMLCanvasElement::createSurfaceFactory(const IntSize& deviceSize, int* msaaSampleCount) const
+{
+    *msaaSampleCount = 0;
+    OwnPtr<RecordingImageBufferFallbackSurfaceFactory> surfaceFactory;
+    if (shouldAccelerate(deviceSize)) {
+        if (document().settings())
+            *msaaSampleCount = document().settings()->accelerated2dCanvasMSAASampleCount();
+        surfaceFactory = adoptPtr(new Accelerated2dSurfaceFactory(*msaaSampleCount));
+    } else {
+        surfaceFactory = adoptPtr(new UnacceleratedSurfaceFactory());
+    }
+    return surfaceFactory.release();
+}
+
+bool HTMLCanvasElement::shouldUseDisplayList(const IntSize& deviceSize)
+{
+    if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
+        return true;
+
+    if (!RuntimeEnabledFeatures::displayList2dCanvasEnabled())
+        return false;
+
+    if (shouldAccelerate(deviceSize))
+        return false;
+
+    return true;
+}
+
 PassOwnPtr<ImageBufferSurface> HTMLCanvasElement::createImageBufferSurface(const IntSize& deviceSize, int* msaaSampleCount)
 {
     OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque;
 
     *msaaSampleCount = 0;
-    if (is3D())
-        return adoptPtr(new WebGLImageBufferSurface(size(), opacityMode));
+    if (is3D()) {
+        // If 3d, but the use of the canvas will be for non-accelerated content
+        // (such as -webkit-canvas, then then make a non-accelerated
+        // ImageBuffer. This means copying the internal Image will require a
+        // pixel readback, but that is unavoidable in this case.
+        // FIXME: Actually, avoid setting m_accelerationDisabled at all when
+        // doing GPU-based rasterization.
+        if (m_accelerationDisabled)
+            return adoptPtr(new UnacceleratedImageBufferSurface(deviceSize, opacityMode));
+        return adoptPtr(new WebGLImageBufferSurface(deviceSize, opacityMode));
+    }
 
-    if (shouldAccelerate(deviceSize)) {
-        if (document().settings())
-            *msaaSampleCount = document().settings()->accelerated2dCanvasMSAASampleCount();
-        OwnPtr<ImageBufferSurface> surface = adoptPtr(new Canvas2DImageBufferSurface(size(), opacityMode, *msaaSampleCount));
+    OwnPtr<RecordingImageBufferFallbackSurfaceFactory> surfaceFactory = createSurfaceFactory(deviceSize, msaaSampleCount);
+
+    if (shouldUseDisplayList(deviceSize)) {
+        OwnPtr<ImageBufferSurface> surface = adoptPtr(new RecordingImageBufferSurface(deviceSize, surfaceFactory.release(), opacityMode));
         if (surface->isValid())
             return surface.release();
+        surfaceFactory = createSurfaceFactory(deviceSize, msaaSampleCount); // recreate because old previous one was released
     }
 
-    return adoptPtr(new UnacceleratedImageBufferSurface(size(), opacityMode));
+    return surfaceFactory->createSurface(deviceSize, opacityMode);
 }
 
 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);
 
@@ -470,7 +584,9 @@ 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;
 
@@ -481,7 +597,9 @@ void HTMLCanvasElement::createImageBuffer()
         return;
     }
 
+    m_imageBuffer->setClient(this);
     m_imageBuffer->context()->setShouldClampToSourceRect(false);
+    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
@@ -492,11 +610,31 @@ void HTMLCanvasElement::createImageBuffer()
     // See CanvasRenderingContext2D::State::State() for more information.
     m_imageBuffer->context()->setMiterLimit(10);
     m_imageBuffer->context()->setStrokeThickness(1);
+#if ENABLE(ASSERT)
+    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)
+{
+#if ENABLE(OILPAN)
+    visitor->trace(m_observers);
+    visitor->trace(m_context);
+#endif
+    DocumentVisibilityObserver::trace(visitor);
+    HTMLElement::trace(visitor);
 }
 
 void HTMLCanvasElement::updateExternallyAllocatedMemory() const
@@ -530,10 +668,8 @@ GraphicsContext* HTMLCanvasElement::drawingContext() const
 
 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
 {
-    if (m_didFailToCreateImageBuffer) {
-        ASSERT(!hasImageBuffer());
-        return 0;
-    }
+    if (!hasImageBuffer())
+        return nullptr;
 
     return drawingContext();
 }
@@ -558,8 +694,11 @@ void HTMLCanvasElement::ensureUnacceleratedImageBuffer()
 Image* HTMLCanvasElement::copiedImage() const
 {
     if (!m_copiedImage && buffer()) {
-        if (m_context)
+        if (m_context && m_context->is3d()) {
+            toWebGLRenderingContext(m_context.get())->setSavingImage(true);
             m_context->paintRenderingResultsToCanvas();
+            toWebGLRenderingContext(m_context.get())->setSavingImage(false);
+        }
         m_copiedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
         updateExternallyAllocatedMemory();
     }
@@ -585,14 +724,22 @@ void HTMLCanvasElement::discardImageBuffer()
 {
     m_contextStateSaver.clear(); // uses context owned by m_imageBuffer
     m_imageBuffer.clear();
+    resetDirtyRect();
     updateExternallyAllocatedMemory();
 }
 
+bool HTMLCanvasElement::hasValidImageBuffer() const
+{
+    return m_imageBuffer && m_imageBuffer->isSurfaceValid();
+}
+
 void HTMLCanvasElement::clearCopiedImage()
 {
-    m_copiedImage.clear();
+    if (m_copiedImage) {
+        m_copiedImage.clear();
+        updateExternallyAllocatedMemory();
+    }
     m_didClearImageBuffer = false;
-    updateExternallyAllocatedMemory();
 }
 
 AffineTransform HTMLCanvasElement::baseTransform() const
@@ -603,16 +750,14 @@ AffineTransform HTMLCanvasElement::baseTransform() const
 
 void HTMLCanvasElement::didChangeVisibilityState(PageVisibilityState visibility)
 {
-    if (hasImageBuffer()) {
-        bool hidden = visibility != PageVisibilityStateVisible;
-        if (hidden) {
-            clearCopiedImage();
-            if (is3D()) {
-                discardImageBuffer();
-            }
-        }
-        if (hasImageBuffer()) {
-            m_imageBuffer->setIsHidden(hidden);
+    if (!m_context)
+        return;
+    bool hidden = visibility != PageVisibilityStateVisible;
+    m_context->setIsHidden(hidden);
+    if (hidden) {
+        clearCopiedImage();
+        if (is3D()) {
+            discardImageBuffer();
         }
     }
 }
@@ -635,18 +780,25 @@ PassRefPtr<Image> HTMLCanvasElement::getSourceImageForCanvas(SourceImageMode mod
         return nullptr;
     }
 
-    if (mode == CopySourceImageIfVolatile) {
-        *status = NormalSourceImageStatus;
-        return copiedImage();
-    }
-
     if (m_context && m_context->is3d()) {
         m_context->paintRenderingResultsToCanvas();
         *status = ExternalSourceImageStatus;
-    } else {
+
+        // can't create SkImage from WebGLImageBufferSurface (contains only SkBitmap)
+        return m_imageBuffer->copyImage(DontCopyBackingStore, Unscaled);
+    }
+
+    RefPtr<SkImage> image = m_imageBuffer->newImageSnapshot();
+    if (image) {
         *status = NormalSourceImageStatus;
+
+        return StaticBitmapImage::create(image.release());
     }
-    return m_imageBuffer->copyImage(DontCopyBackingStore, Unscaled);
+
+
+    *status = InvalidSourceImageStatus;
+
+    return nullptr;
 }
 
 bool HTMLCanvasElement::wouldTaintOrigin(SecurityOrigin*) const