Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
      3  * Copyright (C) 2007 Alp Toker <alp (at) atoker.com>
      4  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "config.h"
     29 #include "core/html/HTMLCanvasElement.h"
     30 
     31 #include "bindings/v8/ExceptionMessages.h"
     32 #include "bindings/v8/ExceptionState.h"
     33 #include "bindings/v8/ScriptController.h"
     34 #include "core/HTMLNames.h"
     35 #include "core/dom/Document.h"
     36 #include "core/dom/ExceptionCode.h"
     37 #include "core/frame/LocalFrame.h"
     38 #include "core/frame/Settings.h"
     39 #include "core/html/ImageData.h"
     40 #include "core/html/canvas/Canvas2DContextAttributes.h"
     41 #include "core/html/canvas/CanvasRenderingContext2D.h"
     42 #include "core/html/canvas/WebGLContextAttributes.h"
     43 #include "core/html/canvas/WebGLContextEvent.h"
     44 #include "core/html/canvas/WebGLRenderingContext.h"
     45 #include "core/rendering/RenderHTMLCanvas.h"
     46 #include "platform/MIMETypeRegistry.h"
     47 #include "platform/RuntimeEnabledFeatures.h"
     48 #include "platform/graphics/Canvas2DImageBufferSurface.h"
     49 #include "platform/graphics/GraphicsContextStateSaver.h"
     50 #include "platform/graphics/ImageBuffer.h"
     51 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
     52 #include "platform/graphics/gpu/WebGLImageBufferSurface.h"
     53 #include "platform/transforms/AffineTransform.h"
     54 #include "public/platform/Platform.h"
     55 #include <math.h>
     56 #include <v8.h>
     57 
     58 namespace WebCore {
     59 
     60 using namespace HTMLNames;
     61 
     62 // These values come from the WhatWG spec.
     63 static const int DefaultWidth = 300;
     64 static const int DefaultHeight = 150;
     65 
     66 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
     67 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
     68 // in exchange for a smaller maximum canvas size.
     69 static const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels
     70 
     71 //In Skia, we will also limit width/height to 32767.
     72 static const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels.
     73 
     74 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver);
     75 
     76 inline HTMLCanvasElement::HTMLCanvasElement(Document& document)
     77     : HTMLElement(canvasTag, document)
     78     , DocumentVisibilityObserver(document)
     79     , m_size(DefaultWidth, DefaultHeight)
     80     , m_ignoreReset(false)
     81     , m_accelerationDisabled(false)
     82     , m_externallyAllocatedMemory(0)
     83     , m_originClean(true)
     84     , m_didFailToCreateImageBuffer(false)
     85     , m_didClearImageBuffer(false)
     86 {
     87     ScriptWrappable::init(this);
     88 }
     89 
     90 DEFINE_NODE_FACTORY(HTMLCanvasElement)
     91 
     92 HTMLCanvasElement::~HTMLCanvasElement()
     93 {
     94     v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory);
     95 #if !ENABLE(OILPAN)
     96     HashSet<RawPtr<CanvasObserver> >::iterator end = m_observers.end();
     97     for (HashSet<RawPtr<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it)
     98         (*it)->canvasDestroyed(this);
     99     // Ensure these go away before the ImageBuffer.
    100     m_contextStateSaver.clear();
    101     m_context.clear();
    102 #endif
    103 }
    104 
    105 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    106 {
    107     if (name == widthAttr || name == heightAttr)
    108         reset();
    109     HTMLElement::parseAttribute(name, value);
    110 }
    111 
    112 RenderObject* HTMLCanvasElement::createRenderer(RenderStyle* style)
    113 {
    114     LocalFrame* frame = document().frame();
    115     if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
    116         return new RenderHTMLCanvas(this);
    117     return HTMLElement::createRenderer(style);
    118 }
    119 
    120 Node::InsertionNotificationRequest HTMLCanvasElement::insertedInto(ContainerNode* node)
    121 {
    122     setIsInCanvasSubtree(true);
    123     return HTMLElement::insertedInto(node);
    124 }
    125 
    126 void HTMLCanvasElement::addObserver(CanvasObserver* observer)
    127 {
    128     m_observers.add(observer);
    129 }
    130 
    131 void HTMLCanvasElement::removeObserver(CanvasObserver* observer)
    132 {
    133     m_observers.remove(observer);
    134 }
    135 
    136 void HTMLCanvasElement::setHeight(int value)
    137 {
    138     setIntegralAttribute(heightAttr, value);
    139 }
    140 
    141 void HTMLCanvasElement::setWidth(int value)
    142 {
    143     setIntegralAttribute(widthAttr, value);
    144 }
    145 
    146 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, CanvasContextAttributes* attrs)
    147 {
    148     // A Canvas can either be "2D" or "webgl" but never both. If you request a 2D canvas and the existing
    149     // context is already 2D, just return that. If the existing context is WebGL, then destroy it
    150     // before creating a new 2D context. Vice versa when requesting a WebGL canvas. Requesting a
    151     // context with any other type string will destroy any existing context.
    152     enum ContextType {
    153         Context2d,
    154         ContextWebkit3d,
    155         ContextExperimentalWebgl,
    156         ContextWebgl,
    157         // Only add new items to the end and keep the order of existing items.
    158         ContextTypeCount,
    159     };
    160 
    161     // FIXME - The code depends on the context not going away once created, to prevent JS from
    162     // seeing a dangling pointer. So for now we will disallow the context from being changed
    163     // once it is created.
    164     if (type == "2d") {
    165         if (m_context && !m_context->is2d())
    166             return 0;
    167         if (!m_context) {
    168             blink::Platform::current()->histogramEnumeration("Canvas.ContextType", Context2d, ContextTypeCount);
    169             m_context = CanvasRenderingContext2D::create(this, static_cast<Canvas2DContextAttributes*>(attrs), document().inQuirksMode());
    170             setNeedsCompositingUpdate();
    171         }
    172         return m_context.get();
    173     }
    174 
    175     // Accept the the provisional "experimental-webgl" or official "webgl" context ID.
    176     ContextType contextType;
    177     bool is3dContext = true;
    178     if (type == "experimental-webgl")
    179         contextType = ContextExperimentalWebgl;
    180     else if (type == "webgl")
    181         contextType = ContextWebgl;
    182     else
    183         is3dContext = false;
    184 
    185     if (is3dContext) {
    186         if (m_context && !m_context->is3d()) {
    187             dispatchEvent(WebGLContextEvent::create(EventTypeNames::webglcontextcreationerror, false, true, "Canvas has an existing, non-WebGL context"));
    188             return 0;
    189         }
    190         if (!m_context) {
    191             blink::Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, ContextTypeCount);
    192             m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs));
    193             setNeedsCompositingUpdate();
    194             updateExternallyAllocatedMemory();
    195         }
    196         return m_context.get();
    197     }
    198     return 0;
    199 }
    200 
    201 void HTMLCanvasElement::didDraw(const FloatRect& rect)
    202 {
    203     clearCopiedImage();
    204 
    205     if (RenderBox* ro = renderBox()) {
    206         FloatRect destRect = ro->contentBoxRect();
    207         FloatRect r = mapRect(rect, FloatRect(0, 0, size().width(), size().height()), destRect);
    208         r.intersect(destRect);
    209         if (r.isEmpty() || m_dirtyRect.contains(r))
    210             return;
    211 
    212         m_dirtyRect.unite(r);
    213         ro->invalidatePaintRectangle(enclosingIntRect(m_dirtyRect));
    214     }
    215 
    216     notifyObserversCanvasChanged(rect);
    217 }
    218 
    219 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect)
    220 {
    221     WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator end = m_observers.end();
    222     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it)
    223         (*it)->canvasChanged(this, rect);
    224 }
    225 
    226 void HTMLCanvasElement::reset()
    227 {
    228     if (m_ignoreReset)
    229         return;
    230 
    231     bool ok;
    232     bool hadImageBuffer = hasImageBuffer();
    233 
    234     int w = getAttribute(widthAttr).toInt(&ok);
    235     if (!ok || w < 0)
    236         w = DefaultWidth;
    237 
    238     int h = getAttribute(heightAttr).toInt(&ok);
    239     if (!ok || h < 0)
    240         h = DefaultHeight;
    241 
    242     if (m_contextStateSaver) {
    243         // Reset to the initial graphics context state.
    244         m_contextStateSaver->restore();
    245         m_contextStateSaver->save();
    246     }
    247 
    248     if (m_context && m_context->is2d())
    249         toCanvasRenderingContext2D(m_context.get())->reset();
    250 
    251     IntSize oldSize = size();
    252     IntSize newSize(w, h);
    253 
    254     // If the size of an existing buffer matches, we can just clear it instead of reallocating.
    255     // This optimization is only done for 2D canvases for now.
    256     if (hadImageBuffer && oldSize == newSize && m_context && m_context->is2d()) {
    257         if (!m_didClearImageBuffer)
    258             clearImageBuffer();
    259         return;
    260     }
    261 
    262     setSurfaceSize(newSize);
    263 
    264     if (m_context && m_context->is3d() && oldSize != size())
    265         toWebGLRenderingContext(m_context.get())->reshape(width(), height());
    266 
    267     if (RenderObject* renderer = this->renderer()) {
    268         if (renderer->isCanvas()) {
    269             if (oldSize != size()) {
    270                 toRenderHTMLCanvas(renderer)->canvasSizeChanged();
    271                 if (renderBox() && renderBox()->hasAcceleratedCompositing())
    272                     renderBox()->contentChanged(CanvasChanged);
    273             }
    274             if (hadImageBuffer)
    275                 renderer->paintInvalidationForWholeRenderer();
    276         }
    277     }
    278 
    279     WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator end = m_observers.end();
    280     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it)
    281         (*it)->canvasResized(this);
    282 }
    283 
    284 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
    285 {
    286     ASSERT(m_context);
    287 
    288     if (!m_context->isAccelerated())
    289         return true;
    290 
    291     if (renderBox() && renderBox()->hasAcceleratedCompositing())
    292         return false;
    293 
    294     return true;
    295 }
    296 
    297 
    298 void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r)
    299 {
    300     // Clear the dirty rect
    301     m_dirtyRect = FloatRect();
    302 
    303     if (context->paintingDisabled())
    304         return;
    305 
    306     if (m_context) {
    307         if (!paintsIntoCanvasBuffer() && !document().printing())
    308             return;
    309         m_context->paintRenderingResultsToCanvas();
    310     }
    311 
    312     if (hasImageBuffer()) {
    313         ImageBuffer* imageBuffer = buffer();
    314         if (imageBuffer) {
    315             CompositeOperator compositeOperator = !m_context || m_context->hasAlpha() ? CompositeSourceOver : CompositeCopy;
    316             if (m_presentedImage)
    317                 context->drawImage(m_presentedImage.get(), pixelSnappedIntRect(r), compositeOperator, DoNotRespectImageOrientation);
    318             else
    319                 context->drawImageBuffer(imageBuffer, pixelSnappedIntRect(r), 0, compositeOperator);
    320         }
    321     } else {
    322         // When alpha is false, we should draw to opaque black.
    323         if (m_context && !m_context->hasAlpha())
    324             context->fillRect(FloatRect(r), Color(0, 0, 0));
    325     }
    326 
    327     if (is3D())
    328         toWebGLRenderingContext(m_context.get())->markLayerComposited();
    329 }
    330 
    331 bool HTMLCanvasElement::is3D() const
    332 {
    333     return m_context && m_context->is3d();
    334 }
    335 
    336 void HTMLCanvasElement::makePresentationCopy()
    337 {
    338     if (!m_presentedImage) {
    339         // The buffer contains the last presented data, so save a copy of it.
    340         m_presentedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
    341         updateExternallyAllocatedMemory();
    342     }
    343 }
    344 
    345 void HTMLCanvasElement::clearPresentationCopy()
    346 {
    347     m_presentedImage.clear();
    348     updateExternallyAllocatedMemory();
    349 }
    350 
    351 void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
    352 {
    353     m_size = size;
    354     m_didFailToCreateImageBuffer = false;
    355     discardImageBuffer();
    356     clearCopiedImage();
    357     if (m_context && m_context->is2d()) {
    358         CanvasRenderingContext2D* context2d = toCanvasRenderingContext2D(m_context.get());
    359         if (context2d->isContextLost()) {
    360             context2d->restoreContext();
    361         }
    362     }
    363 }
    364 
    365 String HTMLCanvasElement::toEncodingMimeType(const String& mimeType)
    366 {
    367     String lowercaseMimeType = mimeType.lower();
    368 
    369     // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread).
    370     if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType))
    371         lowercaseMimeType = "image/png";
    372 
    373     return lowercaseMimeType;
    374 }
    375 
    376 const AtomicString HTMLCanvasElement::imageSourceURL() const
    377 {
    378     return AtomicString(toDataURLInternal("image/png", 0, true));
    379 }
    380 
    381 String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double* quality, bool isSaving) const
    382 {
    383     if (m_size.isEmpty() || !buffer())
    384         return String("data:,");
    385 
    386     String encodingMimeType = toEncodingMimeType(mimeType);
    387 
    388     // Try to get ImageData first, as that may avoid lossy conversions.
    389     RefPtrWillBeRawPtr<ImageData> imageData = getImageData();
    390 
    391     if (imageData)
    392         return ImageDataToDataURL(ImageDataBuffer(imageData->size(), imageData->data()), encodingMimeType, quality);
    393 
    394     if (m_context && m_context->is3d()) {
    395         toWebGLRenderingContext(m_context.get())->setSavingImage(isSaving);
    396         m_context->paintRenderingResultsToCanvas();
    397         toWebGLRenderingContext(m_context.get())->setSavingImage(false);
    398     }
    399 
    400     return buffer()->toDataURL(encodingMimeType, quality);
    401 }
    402 
    403 String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionState& exceptionState) const
    404 {
    405     if (!m_originClean) {
    406         exceptionState.throwSecurityError("Tainted canvases may not be exported.");
    407         return String();
    408     }
    409 
    410     return toDataURLInternal(mimeType, quality);
    411 }
    412 
    413 PassRefPtrWillBeRawPtr<ImageData> HTMLCanvasElement::getImageData() const
    414 {
    415     if (!m_context || !m_context->is3d())
    416         return nullptr;
    417     return toWebGLRenderingContext(m_context.get())->paintRenderingResultsToImageData();
    418 }
    419 
    420 SecurityOrigin* HTMLCanvasElement::securityOrigin() const
    421 {
    422     return document().securityOrigin();
    423 }
    424 
    425 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
    426 {
    427     if (m_context && !m_context->is2d())
    428         return false;
    429 
    430     if (m_accelerationDisabled)
    431         return false;
    432 
    433     Settings* settings = document().settings();
    434     if (!settings || !settings->accelerated2dCanvasEnabled())
    435         return false;
    436 
    437     // Do not use acceleration for small canvas.
    438     if (size.width() * size.height() < settings->minimumAccelerated2dCanvasSize())
    439         return false;
    440 
    441     if (!blink::Platform::current()->canAccelerate2dCanvas())
    442         return false;
    443 
    444     return true;
    445 }
    446 
    447 PassOwnPtr<ImageBufferSurface> HTMLCanvasElement::createImageBufferSurface(const IntSize& deviceSize, int* msaaSampleCount)
    448 {
    449     OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque;
    450 
    451     *msaaSampleCount = 0;
    452     if (is3D())
    453         return adoptPtr(new WebGLImageBufferSurface(size(), opacityMode));
    454 
    455     if (shouldAccelerate(deviceSize)) {
    456         if (document().settings())
    457             *msaaSampleCount = document().settings()->accelerated2dCanvasMSAASampleCount();
    458         OwnPtr<ImageBufferSurface> surface = adoptPtr(new Canvas2DImageBufferSurface(size(), opacityMode, *msaaSampleCount));
    459         if (surface->isValid())
    460             return surface.release();
    461     }
    462 
    463     return adoptPtr(new UnacceleratedImageBufferSurface(size(), opacityMode));
    464 }
    465 
    466 void HTMLCanvasElement::createImageBuffer()
    467 {
    468     createImageBufferInternal();
    469     if (m_didFailToCreateImageBuffer && m_context && m_context->is2d())
    470         toCanvasRenderingContext2D(m_context.get())->loseContext();
    471 }
    472 
    473 void HTMLCanvasElement::createImageBufferInternal()
    474 {
    475     ASSERT(!m_imageBuffer);
    476     ASSERT(!m_contextStateSaver);
    477 
    478     m_didFailToCreateImageBuffer = true;
    479     m_didClearImageBuffer = true;
    480 
    481     IntSize deviceSize = size();
    482     if (deviceSize.width() * deviceSize.height() > MaxCanvasArea)
    483         return;
    484 
    485     if (deviceSize.width() > MaxSkiaDim || deviceSize.height() > MaxSkiaDim)
    486         return;
    487 
    488     if (!deviceSize.width() || !deviceSize.height())
    489         return;
    490 
    491     int msaaSampleCount;
    492     OwnPtr<ImageBufferSurface> surface = createImageBufferSurface(deviceSize, &msaaSampleCount);
    493     if (!surface->isValid())
    494         return;
    495 
    496     m_imageBuffer = ImageBuffer::create(surface.release());
    497     m_imageBuffer->setClient(this);
    498 
    499     m_didFailToCreateImageBuffer = false;
    500 
    501     updateExternallyAllocatedMemory();
    502 
    503     if (is3D()) {
    504         // Early out for WebGL canvases
    505         return;
    506     }
    507 
    508     m_imageBuffer->setClient(this);
    509     m_imageBuffer->context()->setShouldClampToSourceRect(false);
    510     m_imageBuffer->context()->disableAntialiasingOptimizationForHairlineImages();
    511     m_imageBuffer->context()->setImageInterpolationQuality(CanvasDefaultInterpolationQuality);
    512     // Enabling MSAA overrides a request to disable antialiasing. This is true regardless of whether the
    513     // rendering mode is accelerated or not. For consistency, we don't want to apply AA in accelerated
    514     // canvases but not in unaccelerated canvases.
    515     if (!msaaSampleCount && document().settings() && !document().settings()->antialiased2dCanvasEnabled())
    516         m_imageBuffer->context()->setShouldAntialias(false);
    517     // GraphicsContext's defaults don't always agree with the 2d canvas spec.
    518     // See CanvasRenderingContext2D::State::State() for more information.
    519     m_imageBuffer->context()->setMiterLimit(10);
    520     m_imageBuffer->context()->setStrokeThickness(1);
    521 #if ASSERT_ENABLED
    522     m_imageBuffer->context()->disableDestructionChecks(); // 2D canvas is allowed to leave context in an unfinalized state.
    523 #endif
    524     m_contextStateSaver = adoptPtr(new GraphicsContextStateSaver(*m_imageBuffer->context()));
    525 
    526     if (m_context)
    527         setNeedsCompositingUpdate();
    528 }
    529 
    530 void HTMLCanvasElement::notifySurfaceInvalid()
    531 {
    532     if (m_context && m_context->is2d()) {
    533         CanvasRenderingContext2D* context2d = toCanvasRenderingContext2D(m_context.get());
    534         context2d->loseContext();
    535     }
    536 }
    537 
    538 void HTMLCanvasElement::trace(Visitor* visitor)
    539 {
    540     visitor->trace(m_observers);
    541     visitor->trace(m_context);
    542     DocumentVisibilityObserver::trace(visitor);
    543     HTMLElement::trace(visitor);
    544 }
    545 
    546 void HTMLCanvasElement::updateExternallyAllocatedMemory() const
    547 {
    548     int bufferCount = 0;
    549     if (m_imageBuffer)
    550         bufferCount++;
    551     if (is3D())
    552         bufferCount += 2;
    553     if (m_copiedImage)
    554         bufferCount++;
    555     if (m_presentedImage)
    556         bufferCount++;
    557 
    558     Checked<intptr_t, RecordOverflow> checkedExternallyAllocatedMemory = 4 * bufferCount;
    559     checkedExternallyAllocatedMemory *= width();
    560     checkedExternallyAllocatedMemory *= height();
    561     intptr_t externallyAllocatedMemory;
    562     if (checkedExternallyAllocatedMemory.safeGet(externallyAllocatedMemory) == CheckedState::DidOverflow)
    563         externallyAllocatedMemory = std::numeric_limits<intptr_t>::max();
    564 
    565     // Subtracting two intptr_t that are known to be positive will never underflow.
    566     v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(externallyAllocatedMemory - m_externallyAllocatedMemory);
    567     m_externallyAllocatedMemory = externallyAllocatedMemory;
    568 }
    569 
    570 GraphicsContext* HTMLCanvasElement::drawingContext() const
    571 {
    572     return buffer() ? m_imageBuffer->context() : 0;
    573 }
    574 
    575 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
    576 {
    577     if (!hasImageBuffer())
    578         return 0;
    579 
    580     return drawingContext();
    581 }
    582 
    583 ImageBuffer* HTMLCanvasElement::buffer() const
    584 {
    585     if (!hasImageBuffer() && !m_didFailToCreateImageBuffer)
    586         const_cast<HTMLCanvasElement*>(this)->createImageBuffer();
    587     return m_imageBuffer.get();
    588 }
    589 
    590 void HTMLCanvasElement::ensureUnacceleratedImageBuffer()
    591 {
    592     if ((hasImageBuffer() && !m_imageBuffer->isAccelerated()) || m_didFailToCreateImageBuffer)
    593         return;
    594     discardImageBuffer();
    595     OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque;
    596     m_imageBuffer = ImageBuffer::create(size(), opacityMode);
    597     m_didFailToCreateImageBuffer = !m_imageBuffer;
    598 }
    599 
    600 Image* HTMLCanvasElement::copiedImage() const
    601 {
    602     if (!m_copiedImage && buffer()) {
    603         if (m_context)
    604             m_context->paintRenderingResultsToCanvas();
    605         m_copiedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
    606         updateExternallyAllocatedMemory();
    607     }
    608     return m_copiedImage.get();
    609 }
    610 
    611 void HTMLCanvasElement::clearImageBuffer()
    612 {
    613     ASSERT(hasImageBuffer() && !m_didFailToCreateImageBuffer);
    614     ASSERT(!m_didClearImageBuffer);
    615     ASSERT(m_context);
    616 
    617     m_didClearImageBuffer = true;
    618 
    619     if (m_context->is2d()) {
    620         // No need to undo transforms/clip/etc. because we are called right
    621         // after the context is reset.
    622         toCanvasRenderingContext2D(m_context.get())->clearRect(0, 0, width(), height());
    623     }
    624 }
    625 
    626 void HTMLCanvasElement::discardImageBuffer()
    627 {
    628     m_contextStateSaver.clear(); // uses context owned by m_imageBuffer
    629     m_imageBuffer.clear();
    630     updateExternallyAllocatedMemory();
    631 }
    632 
    633 bool HTMLCanvasElement::hasValidImageBuffer() const
    634 {
    635     return m_imageBuffer && m_imageBuffer->isSurfaceValid();
    636 }
    637 
    638 void HTMLCanvasElement::clearCopiedImage()
    639 {
    640     m_copiedImage.clear();
    641     m_didClearImageBuffer = false;
    642     updateExternallyAllocatedMemory();
    643 }
    644 
    645 AffineTransform HTMLCanvasElement::baseTransform() const
    646 {
    647     ASSERT(hasImageBuffer() && !m_didFailToCreateImageBuffer);
    648     return m_imageBuffer->baseTransform();
    649 }
    650 
    651 void HTMLCanvasElement::didChangeVisibilityState(PageVisibilityState visibility)
    652 {
    653     if (hasImageBuffer()) {
    654         bool hidden = visibility != PageVisibilityStateVisible;
    655         if (hidden) {
    656             clearCopiedImage();
    657             if (is3D()) {
    658                 discardImageBuffer();
    659             }
    660         }
    661         if (hasImageBuffer()) {
    662             m_imageBuffer->setIsHidden(hidden);
    663         }
    664     }
    665 }
    666 
    667 void HTMLCanvasElement::didMoveToNewDocument(Document& oldDocument)
    668 {
    669     setObservedDocument(document());
    670     HTMLElement::didMoveToNewDocument(oldDocument);
    671 }
    672 
    673 PassRefPtr<Image> HTMLCanvasElement::getSourceImageForCanvas(SourceImageMode mode, SourceImageStatus* status) const
    674 {
    675     if (!width() || !height()) {
    676         *status = ZeroSizeCanvasSourceImageStatus;
    677         return nullptr;
    678     }
    679 
    680     if (!buffer()) {
    681         *status = InvalidSourceImageStatus;
    682         return nullptr;
    683     }
    684 
    685     if (mode == CopySourceImageIfVolatile) {
    686         *status = NormalSourceImageStatus;
    687         return copiedImage();
    688     }
    689 
    690     if (m_context && m_context->is3d()) {
    691         m_context->paintRenderingResultsToCanvas();
    692         *status = ExternalSourceImageStatus;
    693     } else {
    694         *status = NormalSourceImageStatus;
    695     }
    696     return m_imageBuffer->copyImage(DontCopyBackingStore, Unscaled);
    697 }
    698 
    699 bool HTMLCanvasElement::wouldTaintOrigin(SecurityOrigin*) const
    700 {
    701     return !originClean();
    702 }
    703 
    704 FloatSize HTMLCanvasElement::sourceSize() const
    705 {
    706     return FloatSize(width(), height());
    707 }
    708 
    709 }
    710