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