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