| Index: Source/core/html/canvas/CanvasRenderingContext2D.cpp
|
| diff --git a/Source/core/html/canvas/CanvasRenderingContext2D.cpp b/Source/core/html/canvas/CanvasRenderingContext2D.cpp
|
| index 677c2e496b2033ab0ea155587467767f78c6d02c..7f47bcf0dc9ec0a193bbc5b0573285d7a283dd8e 100644
|
| --- a/Source/core/html/canvas/CanvasRenderingContext2D.cpp
|
| +++ b/Source/core/html/canvas/CanvasRenderingContext2D.cpp
|
| @@ -76,12 +76,26 @@ namespace WebCore {
|
| static const int defaultFontSize = 10;
|
| static const char defaultFontFamily[] = "sans-serif";
|
| static const char defaultFont[] = "10px sans-serif";
|
| +static const double TryRestoreSurfaceInterval = 0.5;
|
| +static const unsigned MaxTryRestoreSurfaceAttemps = 4;
|
| +
|
| +static bool contextLostRestoredEventsEnabled()
|
| +{
|
| + return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
|
| +}
|
|
|
| CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, const Canvas2DContextAttributes* attrs, bool usesCSSCompatibilityParseMode)
|
| : CanvasRenderingContext(canvas)
|
| , m_stateStack(1)
|
| , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
|
| , m_hasAlpha(!attrs || attrs->alpha())
|
| + , m_isContextLost(false)
|
| + , m_contextRestorable(true)
|
| + , m_storageMode(!attrs ? PersistentStorage : attrs->parsedStorage())
|
| + , m_tryRestoreSurfaceAttemptCount(0)
|
| + , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchContextLostEvent)
|
| + , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispatchContextRestoredEvent)
|
| + , m_tryRestoreSurfaceEventTimer(this, &CanvasRenderingContext2D::tryRestoreSurfaceEvent)
|
| {
|
| ScriptWrappable::init(this);
|
| }
|
| @@ -114,6 +128,91 @@ bool CanvasRenderingContext2D::isAccelerated() const
|
| return context && context->isAccelerated();
|
| }
|
|
|
| +bool CanvasRenderingContext2D::isContextLost() const
|
| +{
|
| + return m_isContextLost;
|
| +}
|
| +
|
| +void CanvasRenderingContext2D::loseContext()
|
| +{
|
| + if (m_isContextLost)
|
| + return;
|
| + m_isContextLost = true;
|
| + m_dispatchContextLostEventTimer.startOneShot(0, FROM_HERE);
|
| +}
|
| +
|
| +void CanvasRenderingContext2D::restoreContext()
|
| +{
|
| + if (!m_contextRestorable)
|
| + return;
|
| + // This code path is for restoring from an eviction
|
| + // Restoring from surface failure is handled internally
|
| + ASSERT(m_isContextLost && !canvas()->hasImageBuffer());
|
| +
|
| + if (canvas()->buffer()) {
|
| + if (contextLostRestoredEventsEnabled()) {
|
| + m_dispatchContextRestoredEventTimer.startOneShot(0, FROM_HERE);
|
| + } else {
|
| + // legacy synchronous context restoration.
|
| + reset();
|
| + m_isContextLost = false;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingContext2D>*)
|
| +{
|
| + if (contextLostRestoredEventsEnabled()) {
|
| + RefPtr<Event> event(Event::createCancelable(EventTypeNames::contextlost));
|
| + canvas()->dispatchEvent(event);
|
| + if (event->defaultPrevented()) {
|
| + m_contextRestorable = false;
|
| + }
|
| + }
|
| +
|
| + // If an image buffer is present, it means the context was not lost due to
|
| + // an eviction, but rather due to a surface failure (gpu context lost?)
|
| + if (m_contextRestorable && canvas()->hasImageBuffer()) {
|
| + m_tryRestoreSurfaceAttemptCount = 0;
|
| + m_tryRestoreSurfaceEventTimer.startRepeating(TryRestoreSurfaceInterval, FROM_HERE);
|
| + }
|
| +}
|
| +
|
| +void CanvasRenderingContext2D::tryRestoreSurfaceEvent(Timer<CanvasRenderingContext2D>* timer)
|
| +{
|
| + if (!m_isContextLost) {
|
| + // Canvas was already restored (possibly thanks to a resize), so stop trying.
|
| + m_tryRestoreSurfaceEventTimer.stop();
|
| + return;
|
| + }
|
| + if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) {
|
| + m_tryRestoreSurfaceEventTimer.stop();
|
| + dispatchContextRestoredEvent(0);
|
| + }
|
| +
|
| + if (++m_tryRestoreSurfaceAttemptCount > MaxTryRestoreSurfaceAttemps)
|
| + canvas()->discardImageBuffer();
|
| +
|
| + if (!canvas()->hasImageBuffer()) {
|
| + // final attempt: allocate a brand new image buffer instead of restoring
|
| + timer->stop();
|
| + if (canvas()->buffer())
|
| + dispatchContextRestoredEvent(0);
|
| + }
|
| +}
|
| +
|
| +void CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderingContext2D>*)
|
| +{
|
| + if (!m_isContextLost)
|
| + return;
|
| + reset();
|
| + m_isContextLost = false;
|
| + if (contextLostRestoredEventsEnabled()) {
|
| + RefPtr<Event> event(Event::create(EventTypeNames::contextrestored));
|
| + canvas()->dispatchEvent(event);
|
| + }
|
| +}
|
| +
|
| void CanvasRenderingContext2D::reset()
|
| {
|
| unwindStateStack();
|
| @@ -1665,6 +1764,8 @@ void CanvasRenderingContext2D::didDraw(const FloatRect& dirtyRect)
|
|
|
| GraphicsContext* CanvasRenderingContext2D::drawingContext() const
|
| {
|
| + if (isContextLost())
|
| + return 0;
|
| return canvas()->drawingContext();
|
| }
|
|
|
| @@ -1755,7 +1856,7 @@ PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy,
|
|
|
| IntRect imageDataRect = enclosingIntRect(logicalRect);
|
| ImageBuffer* buffer = canvas()->buffer();
|
| - if (!buffer)
|
| + if (!buffer || isContextLost())
|
| return createEmptyImageData(imageDataRect.size());
|
|
|
| RefPtr<Uint8ClampedArray> byteArray = buffer->getUnmultipliedImageData(imageDataRect);
|
|
|