diff options
Diffstat (limited to 'Source/WebCore/html/canvas/WebGLRenderingContext.cpp')
-rw-r--r-- | Source/WebCore/html/canvas/WebGLRenderingContext.cpp | 523 |
1 files changed, 331 insertions, 192 deletions
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp index 933d37f..7e7bfe3 100644 --- a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -1,5 +1,7 @@ /* * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012 Sony Ericsson Mobile Communications AB + * Copyright (C) 2012 Sony Mobile Communications AB * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,6 +53,7 @@ #include "RenderLayer.h" #include "Settings.h" #include "Uint16Array.h" +#include "Uint32Array.h" #include "WebGLActiveInfo.h" #include "WebGLBuffer.h" #include "WebGLContextAttributes.h" @@ -328,29 +331,14 @@ private: void WebGLRenderingContext::WebGLRenderingContextRestoreTimer::fired() { - // Timer is started when m_contextLost is false. It will first call - // onLostContext, which will set m_contextLost to true. Then it will keep - // calling restoreContext and reschedule itself until m_contextLost is back - // to false. - if (!m_context->m_contextLost) { - m_context->onLostContext(); - startOneShot(secondsBetweenRestoreAttempts); - } else { - // The rendering context is not restored if there is no handler for - // the context restored event. - if (!m_context->canvas()->hasEventListeners(eventNames().webglcontextrestoredEvent)) - return; - - m_context->restoreContext(); - if (m_context->m_contextLost) - startOneShot(secondsBetweenRestoreAttempts); - } + m_context->maybeRestoreContext(RealLostContext); } class WebGLRenderingContextLostCallback : public GraphicsContext3D::ContextLostCallback { public: WebGLRenderingContextLostCallback(WebGLRenderingContext* cb) : m_contextLostCallback(cb) {} - virtual void onContextLost() { m_contextLostCallback->forceLostContext(); } + virtual void onContextLost() { + m_contextLostCallback->forceLostContext(WebGLRenderingContext::RealLostContext); } virtual ~WebGLRenderingContextLostCallback() {} private: WebGLRenderingContext* m_contextLostCallback; @@ -367,7 +355,11 @@ PassOwnPtr<WebGLRenderingContext> WebGLRenderingContext::create(HTMLCanvasElemen attributes.antialias = false; } +#if PLATFORM(ANDROID) + RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(canvas, attributes, 0)); +#else RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(attributes, hostWindow)); +#endif if (!context) { canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextcreationerrorEvent, false, true, "Could not create a WebGL context.")); @@ -381,6 +373,7 @@ WebGLRenderingContext::WebGLRenderingContext(HTMLCanvasElement* passedCanvas, Pa GraphicsContext3D::Attributes attributes) : CanvasRenderingContext(passedCanvas) , m_context(context) + , m_restoreAllowed(false) , m_restoreTimer(this) , m_videoCache(4) , m_contextLost(false) @@ -554,9 +547,10 @@ void WebGLRenderingContext::paintRenderingResultsToCanvas() { // Until the canvas is written to by the application, the clear that // happened after it was composited should be ignored by the compositor. - if (m_context->layerComposited() && !m_attributes.preserveDrawingBuffer) + if (m_context->layerComposited() && !m_attributes.preserveDrawingBuffer) { + m_context->paintCompositedResultsToCanvas(this); canvas()->makePresentationCopy(); - else + } else canvas()->clearPresentationCopy(); clearIfComposited(); if (!m_markedCanvasDirty && !m_layerCleared) @@ -577,6 +571,18 @@ bool WebGLRenderingContext::paintsIntoCanvasBuffer() const return m_context->paintsIntoCanvasBuffer(); } +#if PLATFORM(ANDROID) +void WebGLRenderingContext::recreateSurface() +{ + m_context->recreateSurface(); +} + +void WebGLRenderingContext::releaseSurface() +{ + m_context->releaseSurface(); +} +#endif + void WebGLRenderingContext::reshape(int width, int height) { if (m_needsUpdate) { @@ -593,6 +599,20 @@ void WebGLRenderingContext::reshape(int width, int height) m_context->reshape(width, height); } +GC3Dsizei WebGLRenderingContext::drawingBufferWidth() +{ + if (isContextLost()) + return 0; + return m_context->getInternalFramebufferSize().width(); +} + +GC3Dsizei WebGLRenderingContext::drawingBufferHeight() +{ + if (isContextLost()) + return 0; + return m_context->getInternalFramebufferSize().height(); +} + unsigned int WebGLRenderingContext::sizeInBytes(GC3Denum type) { switch (type) { @@ -648,6 +668,8 @@ void WebGLRenderingContext::bindAttribLocation(WebGLProgram* program, GC3Duint i UNUSED_PARAM(ec); if (isContextLost() || !validateWebGLObject(program)) return; + if (!validateLocationLength(name)) + return; if (!validateString(name)) return; m_context->bindAttribLocation(objectOrZero(program), index, name); @@ -814,7 +836,7 @@ void WebGLRenderingContext::blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::bufferData(GC3Denum target, GC3Dsizeiptr size, GC3Denum usage, ExceptionCode& ec) +void WebGLRenderingContext::bufferData(GC3Denum target, long long size, GC3Denum usage, ExceptionCode& ec) { UNUSED_PARAM(ec); if (isContextLost()) @@ -827,13 +849,13 @@ void WebGLRenderingContext::bufferData(GC3Denum target, GC3Dsizeiptr size, GC3De return; } if (!isErrorGeneratedOnOutOfBoundsAccesses()) { - if (!buffer->associateBufferData(size)) { + if (!buffer->associateBufferData(static_cast<GC3Dsizeiptr>(size))) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } } - m_context->bufferData(target, size, usage); + m_context->bufferData(target, static_cast<GC3Dsizeiptr>(size), usage); cleanupAfterGraphicsCall(false); } @@ -856,7 +878,9 @@ void WebGLRenderingContext::bufferData(GC3Denum target, ArrayBuffer* data, GC3De } } - m_context->bufferData(target, data->byteLength(), data->data(), usage); + // Some platforms incorrectly signal GL_OUT_OF_MEMORY if size == 0 + if (data->byteLength() > 0) + m_context->bufferData(target, data->byteLength(), data->data(), usage); cleanupAfterGraphicsCall(false); } @@ -879,11 +903,13 @@ void WebGLRenderingContext::bufferData(GC3Denum target, ArrayBufferView* data, G } } - m_context->bufferData(target, data->byteLength(), data->baseAddress(), usage); + // Some platforms incorrectly signal GL_OUT_OF_MEMORY if size == 0 + if (data->byteLength() > 0) + m_context->bufferData(target, data->byteLength(), data->baseAddress(), usage); cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::bufferSubData(GC3Denum target, GC3Dintptr offset, ArrayBuffer* data, ExceptionCode& ec) +void WebGLRenderingContext::bufferSubData(GC3Denum target, long long offset, ArrayBuffer* data, ExceptionCode& ec) { UNUSED_PARAM(ec); if (isContextLost()) @@ -898,17 +924,17 @@ void WebGLRenderingContext::bufferSubData(GC3Denum target, GC3Dintptr offset, Ar if (!data) return; if (!isErrorGeneratedOnOutOfBoundsAccesses()) { - if (!buffer->associateBufferSubData(offset, data)) { + if (!buffer->associateBufferSubData(static_cast<GC3Dintptr>(offset), data)) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } } - m_context->bufferSubData(target, offset, data->byteLength(), data->data()); + m_context->bufferSubData(target, static_cast<GC3Dintptr>(offset), data->byteLength(), data->data()); cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::bufferSubData(GC3Denum target, GC3Dintptr offset, ArrayBufferView* data, ExceptionCode& ec) +void WebGLRenderingContext::bufferSubData(GC3Denum target, long long offset, ArrayBufferView* data, ExceptionCode& ec) { UNUSED_PARAM(ec); if (isContextLost()) @@ -923,13 +949,13 @@ void WebGLRenderingContext::bufferSubData(GC3Denum target, GC3Dintptr offset, Ar if (!data) return; if (!isErrorGeneratedOnOutOfBoundsAccesses()) { - if (!buffer->associateBufferSubData(offset, data)) { + if (!buffer->associateBufferSubData(static_cast<GC3Dintptr>(offset), data)) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } } - m_context->bufferSubData(target, offset, data->byteLength(), data->baseAddress()); + m_context->bufferSubData(target, static_cast<GC3Dintptr>(offset), data->byteLength(), data->baseAddress()); cleanupAfterGraphicsCall(false); } @@ -943,9 +969,10 @@ GC3Denum WebGLRenderingContext::checkFramebufferStatus(GC3Denum target) } if (!m_framebufferBinding || !m_framebufferBinding->object()) return GraphicsContext3D::FRAMEBUFFER_COMPLETE; - if (m_framebufferBinding->isIncomplete(true)) - return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; - unsigned long result = m_context->checkFramebufferStatus(target); + GC3Denum result = m_framebufferBinding->checkStatus(); + if (result != GraphicsContext3D::FRAMEBUFFER_COMPLETE) + return result; + result = m_context->checkFramebufferStatus(target); cleanupAfterGraphicsCall(false); return result; } @@ -1026,6 +1053,34 @@ void WebGLRenderingContext::compileShader(WebGLShader* shader, ExceptionCode& ec cleanupAfterGraphicsCall(false); } +void WebGLRenderingContext::compressedTexImage2D(GC3Denum target, GC3Dint level, + GC3Denum internalformat, GC3Dsizei width, + GC3Dsizei height, GC3Dint border, + ArrayBufferView* data) +{ + if (isContextLost()) + return; + if (!validateTexFuncLevel(target, level)) + return; + + // Currently, we have no support for compressed textures: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); +} + +void WebGLRenderingContext::compressedTexSubImage2D(GC3Denum target, GC3Dint level, + GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, + GC3Denum format, ArrayBufferView* data) +{ + if (isContextLost()) + return; + if (!validateTexFuncLevel(target, level)) + return; + + // Currently, we have no support for compressed textures: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); +} + void WebGLRenderingContext::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border) { if (isContextLost()) @@ -1048,8 +1103,10 @@ void WebGLRenderingContext::copyTexImage2D(GC3Denum target, GC3Dint level, GC3De return; } clearIfComposited(); - if (isResourceSafe()) - m_context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); + if (isResourceSafe()) { + if (width > 0 && height > 0) + m_context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); + } else { GC3Dint clippedX, clippedY; GC3Dsizei clippedWidth, clippedHeight; @@ -1060,8 +1117,11 @@ void WebGLRenderingContext::copyTexImage2D(GC3Denum target, GC3Dint level, GC3De m_context->copyTexSubImage2D(target, level, clippedX - x, clippedY - y, clippedX, clippedY, clippedWidth, clippedHeight); } - } else - m_context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); + } else { + if (width > 0 && height > 0) + m_context->copyTexImage2D(target, level, internalformat, + x, y, width, height, border); + } } // FIXME: if the framebuffer is not complete, none of the below should be executed. tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); @@ -1256,7 +1316,7 @@ void WebGLRenderingContext::deleteRenderbuffer(WebGLRenderbuffer* renderbuffer) if (renderbuffer == m_renderbufferBinding) m_renderbufferBinding = 0; if (m_framebufferBinding) - m_framebufferBinding->removeAttachment(renderbuffer); + m_framebufferBinding->removeAttachmentFromBoundFramebuffer(renderbuffer); } void WebGLRenderingContext::deleteShader(WebGLShader* shader) @@ -1275,7 +1335,7 @@ void WebGLRenderingContext::deleteTexture(WebGLTexture* texture) m_textureUnits[i].m_textureCubeMapBinding = 0; } if (m_framebufferBinding) - m_framebufferBinding->removeAttachment(texture); + m_framebufferBinding->removeAttachmentFromBoundFramebuffer(texture); } void WebGLRenderingContext::depthFunc(GC3Denum func) @@ -1586,7 +1646,7 @@ void WebGLRenderingContext::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei c cleanupAfterGraphicsCall(true); } -void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, ExceptionCode& ec) +void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, ExceptionCode& ec) { UNUSED_PARAM(ec); @@ -1621,14 +1681,14 @@ void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denu int numElements = 0; if (!isErrorGeneratedOnOutOfBoundsAccesses()) { // Ensure we have a valid rendering state - if (!validateElementArraySize(count, type, offset)) { + if (!validateElementArraySize(count, type, static_cast<GC3Dintptr>(offset))) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } if (!count) return; if (!validateIndexArrayConservative(type, numElements) || !validateRenderingState(numElements)) { - if (!validateIndexArrayPrecise(count, type, offset, numElements) || !validateRenderingState(numElements)) { + if (!validateIndexArrayPrecise(count, type, static_cast<GC3Dintptr>(offset), numElements) || !validateRenderingState(numElements)) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } @@ -1649,12 +1709,12 @@ void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denu bool vertexAttrib0Simulated = false; if (!isGLES2Compliant()) { if (!numElements) - validateIndexArrayPrecise(count, type, offset, numElements); + validateIndexArrayPrecise(count, type, static_cast<GC3Dintptr>(offset), numElements); vertexAttrib0Simulated = simulateVertexAttrib0(numElements); } if (!isGLES2NPOTStrict()) handleNPOTTextures(true); - m_context->drawElements(mode, count, type, offset); + m_context->drawElements(mode, count, type, static_cast<GC3Dintptr>(offset)); if (!isGLES2Compliant() && vertexAttrib0Simulated) restoreStatesAfterVertexAttrib0Simulation(); if (!isGLES2NPOTStrict()) @@ -1752,7 +1812,7 @@ void WebGLRenderingContext::framebufferRenderbuffer(GC3Denum target, GC3Denum at default: m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, objectOrZero(buffer)); } - m_framebufferBinding->setAttachment(attachment, buffer); + m_framebufferBinding->setAttachmentForBoundFramebuffer(attachment, buffer); if (reattachDepth) { Platform3DObject object = objectOrZero(m_framebufferBinding->getAttachment(GraphicsContext3D::DEPTH_ATTACHMENT)); if (object) @@ -1797,7 +1857,7 @@ void WebGLRenderingContext::framebufferTexture2D(GC3Denum target, GC3Denum attac return; } m_context->framebufferTexture2D(target, attachment, textarget, objectOrZero(texture), level); - m_framebufferBinding->setAttachment(attachment, textarget, texture, level); + m_framebufferBinding->setAttachmentForBoundFramebuffer(attachment, textarget, texture, level); cleanupAfterGraphicsCall(false); } @@ -1869,23 +1929,15 @@ bool WebGLRenderingContext::getAttachedShaders(WebGLProgram* program, Vector<Web shaderObjects.clear(); if (isContextLost() || !validateWebGLObject(program)) return false; - GC3Dint numShaders = 0; - m_context->getProgramiv(objectOrZero(program), GraphicsContext3D::ATTACHED_SHADERS, &numShaders); - if (numShaders) { - OwnArrayPtr<Platform3DObject> shaders = adoptArrayPtr(new Platform3DObject[numShaders]); - GC3Dsizei count = 0; - m_context->getAttachedShaders(objectOrZero(program), numShaders, &count, shaders.get()); - if (count != numShaders) - return false; - shaderObjects.resize(numShaders); - for (GC3Dint ii = 0; ii < numShaders; ++ii) { - WebGLShader* shader = findShader(shaders[ii]); - if (!shader) { - shaderObjects.clear(); - return false; - } - shaderObjects[ii] = shader; - } + + const GC3Denum shaderType[] = { + GraphicsContext3D::VERTEX_SHADER, + GraphicsContext3D::FRAGMENT_SHADER + }; + for (unsigned i = 0; i < sizeof(shaderType) / sizeof(GC3Denum); ++i) { + WebGLShader* shader = program->getAttachedShader(shaderType[i]); + if (shader) + shaderObjects.append(shader); } return true; } @@ -1894,6 +1946,8 @@ GC3Dint WebGLRenderingContext::getAttribLocation(WebGLProgram* program, const St { if (isContextLost()) return -1; + if (!validateLocationLength(name)) + return -1; if (!validateString(name)) return -1; return m_context->getAttribLocation(objectOrZero(program), name); @@ -1914,6 +1968,12 @@ WebGLGetInfo WebGLRenderingContext::getBufferParameter(GC3Denum target, GC3Denum return WebGLGetInfo(); } + // Some platforms fail to raise INVALID_OPERATION if no array buffer is bound + if (target == GraphicsContext3D::ARRAY_BUFFER && !m_boundArrayBuffer) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return WebGLGetInfo(); + } + WebGLStateRestorer(this, false); GC3Dint value = 0; m_context->getBufferParameteriv(target, pname, &value); @@ -1979,46 +2039,51 @@ WebGLGetInfo WebGLRenderingContext::getFramebufferAttachmentParameter(GC3Denum t UNUSED_PARAM(ec); if (isContextLost() || !validateFramebufferFuncParameters(target, attachment)) return WebGLGetInfo(); - switch (pname) { - case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: - case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: - break; - default: - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); - return WebGLGetInfo(); - } - if (!m_framebufferBinding || !m_framebufferBinding->object() || m_framebufferBinding->isIncomplete(false)) { + if (!m_framebufferBinding || !m_framebufferBinding->object()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return WebGLGetInfo(); } - if (pname != GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { - WebGLStateRestorer(this, false); - GC3Dint value = 0; - m_context->getFramebufferAttachmentParameteriv(target, attachment, pname, &value); + WebGLObject* object = m_framebufferBinding->getAttachment(attachment); + if (!object) { if (pname == GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) - return WebGLGetInfo(static_cast<unsigned int>(value)); - return WebGLGetInfo(value); + return WebGLGetInfo(static_cast<unsigned int>(GraphicsContext3D::NONE)); + // OpenGL ES 2.0 specifies INVALID_ENUM in this case, while desktop GL + // specifies INVALID_OPERATION. + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); } - WebGLStateRestorer(this, false); - GC3Dint type = 0; - m_context->getFramebufferAttachmentParameteriv(target, attachment, GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type); - if (!type) - return WebGLGetInfo(); - GC3Dint value = 0; - m_context->getFramebufferAttachmentParameteriv(target, attachment, GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &value); - switch (type) { - case GraphicsContext3D::RENDERBUFFER: - return WebGLGetInfo(PassRefPtr<WebGLRenderbuffer>(findRenderbuffer(static_cast<Platform3DObject>(value)))); - case GraphicsContext3D::TEXTURE: - return WebGLGetInfo(PassRefPtr<WebGLTexture>(findTexture(static_cast<Platform3DObject>(value)))); - default: - // FIXME: raise exception? - return WebGLGetInfo(); + ASSERT(object->isTexture() || object->isRenderbuffer()); + if (object->isTexture()) { + switch (pname) { + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return WebGLGetInfo(static_cast<unsigned int>(GraphicsContext3D::TEXTURE)); + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return WebGLGetInfo(PassRefPtr<WebGLTexture>(reinterpret_cast<WebGLTexture*>(object))); + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: + { + WebGLStateRestorer(this, false); + GC3Dint value = 0; + m_context->getFramebufferAttachmentParameteriv(target, attachment, pname, &value); + return WebGLGetInfo(value); + } + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + } else { + switch (pname) { + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return WebGLGetInfo(static_cast<unsigned int>(GraphicsContext3D::RENDERBUFFER)); + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return WebGLGetInfo(PassRefPtr<WebGLRenderbuffer>(reinterpret_cast<WebGLRenderbuffer*>(object))); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } } } @@ -2062,8 +2127,8 @@ WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& case GraphicsContext3D::COLOR_WRITEMASK: return getBooleanArrayParameter(pname); case GraphicsContext3D::COMPRESSED_TEXTURE_FORMATS: - // Defined as null in the spec - return WebGLGetInfo(); + // Currently, we have no support for compressed textures: + return WebGLGetInfo(Uint32Array::create(0, 0)); case GraphicsContext3D::CULL_FACE: return getBooleanParameter(pname); case GraphicsContext3D::CULL_FACE_MODE: @@ -2558,6 +2623,8 @@ PassRefPtr<WebGLUniformLocation> WebGLRenderingContext::getUniformLocation(WebGL UNUSED_PARAM(ec); if (isContextLost() || !validateWebGLObject(program)) return 0; + if (!validateLocationLength(name)) + return 0; if (!validateString(name)) return 0; WebGLStateRestorer(this, false); @@ -2603,13 +2670,13 @@ WebGLGetInfo WebGLRenderingContext::getVertexAttrib(GC3Duint index, GC3Denum pna } } -GC3Dsizeiptr WebGLRenderingContext::getVertexAttribOffset(GC3Duint index, GC3Denum pname) +long long WebGLRenderingContext::getVertexAttribOffset(GC3Duint index, GC3Denum pname) { if (isContextLost()) return 0; GC3Dsizeiptr result = m_context->getVertexAttribOffset(index, pname); cleanupAfterGraphicsCall(false); - return result; + return static_cast<long long>(result); } void WebGLRenderingContext::hint(GC3Denum target, GC3Denum mode) @@ -2647,14 +2714,6 @@ GC3Dboolean WebGLRenderingContext::isBuffer(WebGLBuffer* buffer) bool WebGLRenderingContext::isContextLost() { - if (m_restoreTimer.isActive()) - return true; - - bool newContextLost = m_context->getExtensions()->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR; - - if (newContextLost != m_contextLost) - m_restoreTimer.startOneShot(secondsBetweenRestoreAttempts); - return m_contextLost; } @@ -2736,12 +2795,6 @@ void WebGLRenderingContext::linkProgram(WebGLProgram* program, ExceptionCode& ec m_context->linkProgram(objectOrZero(program)); program->increaseLinkCount(); - // cache link status - GC3Dint value = 0; - m_context->getProgramiv(objectOrZero(program), GraphicsContext3D::LINK_STATUS, &value); - program->setLinkStatus(static_cast<bool>(value)); - // Need to cache link status before caching active attribute locations. - program->cacheActiveAttribLocations(); cleanupAfterGraphicsCall(false); } @@ -2792,14 +2845,13 @@ void WebGLRenderingContext::polygonOffset(GC3Dfloat factor, GC3Dfloat units) cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode& ec) +void WebGLRenderingContext::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode&) { if (isContextLost()) return; - if (!canvas()->originClean()) { - ec = SECURITY_ERR; - return; - } + // Due to WebGL's same-origin restrictions, it is not possible to + // taint the origin using the WebGL API. + ASSERT(canvas()->originClean()); // Validate input parameters. if (!pixels) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); @@ -2851,7 +2903,51 @@ void WebGLRenderingContext::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC } clearIfComposited(); void* data = pixels->baseAddress(); - m_context->readPixels(x, y, width, height, format, type, data); + + // WebGL requires that areas lying outside the frame buffer should be filled with 0. + // Most OpenGL platforms do not support this directly. + GC3Dint clippedX, clippedY; + GC3Dsizei clippedWidth, clippedHeight; + if (clip2D(x, y, width, height, getBoundFramebufferWidth(), getBoundFramebufferHeight(), + &clippedX, &clippedY, &clippedWidth, &clippedHeight)) { + unsigned int padding = 0; + m_context->computeImageSizeInBytes(format, type, clippedWidth, clippedHeight, + m_packAlignment, &totalBytesRequired, &padding); + char *tmp = (char *)fastMalloc(totalBytesRequired); + // Some platforms incorrectly signal GL_INVALID_VALUE if width == 0 || height == 0 + if (clippedWidth > 0 && clippedHeight > 0) + m_context->readPixels(clippedX, clippedY, clippedWidth, + clippedHeight, format, type, tmp); + + unsigned int bytesPerComponent, componentsPerPixel; + m_context->computeFormatAndTypeParameters(format, type, &componentsPerPixel, + &bytesPerComponent); + int clippedRowBytes = bytesPerComponent * componentsPerPixel * clippedWidth; + int clippedStride = clippedRowBytes + padding; + int rowBytes = bytesPerComponent * componentsPerPixel * width; + int stride = rowBytes + padding; + char *src = tmp; + char *dst = (char *)data; + int xdelta = (clippedX - x) * bytesPerComponent * componentsPerPixel; + for (int r = y; r < y + height; r++) { + if (r < y + height - 1) + memset(dst, 0, stride); + else + memset(dst, 0, rowBytes); + if (r >= clippedY && r < clippedY + clippedHeight) { + memcpy(dst + xdelta, src, clippedRowBytes); + src += clippedStride; + } + dst += stride; + } + fastFree(tmp); + } + else { + // Some platforms incorrectly signal GL_INVALID_VALUE if width == 0 || height == 0 + if (width > 0 && height > 0) + m_context->readPixels(x, y, width, height, format, type, data); + } + #if OS(DARWIN) // FIXME: remove this section when GL driver bug on Mac is fixed, i.e., // when alpha is off, readPixels should set alpha to 255 instead of 0. @@ -3142,7 +3238,11 @@ void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum return; if (!validateHTMLImageElement(image)) return; - checkOrigin(image); + if (wouldTaintOrigin(image)) { + ec = SECURITY_ERR; + return; + } + texImage2DImpl(target, level, internalformat, format, type, image->cachedImage()->image(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); } @@ -3157,7 +3257,10 @@ void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } - checkOrigin(canvas); + if (wouldTaintOrigin(canvas)) { + ec = SECURITY_ERR; + return; + } RefPtr<ImageData> imageData = canvas->getImageData(); if (imageData) texImage2D(target, level, internalformat, format, type, imageData.get(), ec); @@ -3167,7 +3270,7 @@ void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum } #if ENABLE(VIDEO) -PassRefPtr<Image> WebGLRenderingContext::videoFrameToImage(HTMLVideoElement* video) +PassRefPtr<Image> WebGLRenderingContext::videoFrameToImage(HTMLVideoElement* video, ExceptionCode& ec) { if (!video || !video->videoWidth() || !video->videoHeight()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); @@ -3179,7 +3282,10 @@ PassRefPtr<Image> WebGLRenderingContext::videoFrameToImage(HTMLVideoElement* vid m_context->synthesizeGLError(GraphicsContext3D::OUT_OF_MEMORY); return 0; } - checkOrigin(video); + if (wouldTaintOrigin(video)) { + ec = SECURITY_ERR; + return 0; + } IntRect destRect(0, 0, size.width(), size.height()); // FIXME: Turn this into a GPU-GPU texture copy instead of CPU readback. video->paintCurrentFrameInContext(buf->context(), destRect); @@ -3192,8 +3298,8 @@ void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum ec = 0; if (isContextLost()) return; - RefPtr<Image> image = videoFrameToImage(video); - if (!video) + RefPtr<Image> image = videoFrameToImage(video, ec); + if (!image) return; texImage2DImpl(target, level, internalformat, format, type, image.get(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); } @@ -3334,7 +3440,10 @@ void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Din return; if (!validateHTMLImageElement(image)) return; - checkOrigin(image); + if (wouldTaintOrigin(image)) { + ec = SECURITY_ERR; + return; + } texSubImage2DImpl(target, level, xoffset, yoffset, format, type, image->cachedImage()->image(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); } @@ -3349,7 +3458,10 @@ void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Din m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } - checkOrigin(canvas); + if (wouldTaintOrigin(canvas)) { + ec = SECURITY_ERR; + return; + } RefPtr<ImageData> imageData = canvas->getImageData(); if (imageData) texSubImage2D(target, level, xoffset, yoffset, format, type, imageData.get(), ec); @@ -3365,8 +3477,8 @@ void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Din ec = 0; if (isContextLost()) return; - RefPtr<Image> image = videoFrameToImage(video); - if (!video) + RefPtr<Image> image = videoFrameToImage(video, ec); + if (!image) return; texSubImage2DImpl(target, level, xoffset, yoffset, format, type, image.get(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); } @@ -3799,7 +3911,7 @@ void WebGLRenderingContext::vertexAttrib4fv(GC3Duint index, GC3Dfloat* v, GC3Dsi vertexAttribfvImpl(index, v, size, 4); } -void WebGLRenderingContext::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, GC3Dintptr offset, ExceptionCode& ec) +void WebGLRenderingContext::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, long long offset, ExceptionCode& ec) { UNUSED_PARAM(ec); if (isContextLost()) @@ -3833,7 +3945,7 @@ void WebGLRenderingContext::vertexAttribPointer(GC3Duint index, GC3Dint size, GC m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return; } - if ((stride % typeSize) || (offset % typeSize)) { + if ((stride % typeSize) || (static_cast<GC3Dintptr>(offset) % typeSize)) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } @@ -3849,8 +3961,8 @@ void WebGLRenderingContext::vertexAttribPointer(GC3Duint index, GC3Dint size, GC state.normalized = normalized; state.stride = validatedStride; state.originalStride = stride; - state.offset = offset; - m_context->vertexAttribPointer(index, size, type, normalized, stride, offset); + state.offset = static_cast<GC3Dintptr>(offset); + m_context->vertexAttribPointer(index, size, type, normalized, stride, static_cast<GC3Dintptr>(offset)); cleanupAfterGraphicsCall(false); } @@ -3872,17 +3984,30 @@ void WebGLRenderingContext::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3D cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::forceLostContext() +void WebGLRenderingContext::forceLostContext(WebGLRenderingContext::LostContextMode mode) { if (isContextLost()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } - m_restoreTimer.startOneShot(0); + loseContext(); + + if (mode == RealLostContext) + m_restoreTimer.startOneShot(0); } -void WebGLRenderingContext::onLostContext() +void WebGLRenderingContext::forceRestoreContext() +{ + if (!isContextLost()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + maybeRestoreContext(SyntheticLostContext); +} + +void WebGLRenderingContext::loseContext() { m_contextLost = true; @@ -3890,7 +4015,7 @@ void WebGLRenderingContext::onLostContext() // There is no direct way to clear errors from a GL implementation and // looping until getError() becomes NO_ERROR might cause an infinite loop if - // the driver or context implementation had a bug. So, loop a reasonably + // the driver or context implementation had a bug. So, loop a reasonably // large number of times to clear any existing errors. for (int i = 0; i < 100; ++i) { if (m_context->getError() == GraphicsContext3D::NO_ERROR) @@ -3898,14 +4023,67 @@ void WebGLRenderingContext::onLostContext() } m_context->synthesizeGLError(GraphicsContext3D::CONTEXT_LOST_WEBGL); - canvas()->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextlostEvent, false, true, "")); + RefPtr<WebGLContextEvent> event = WebGLContextEvent::create(eventNames().webglcontextlostEvent, false, true, ""); + canvas()->dispatchEvent(event); + m_restoreAllowed = event->defaultPrevented(); } -void WebGLRenderingContext::restoreContext() +void WebGLRenderingContext::maybeRestoreContext(WebGLRenderingContext::LostContextMode mode) { + if (!m_contextLost) { + ASSERT(mode == SyntheticLostContext); + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + // The rendering context is not restored unless the default + // behavior of the webglcontextlost event was prevented earlier. + if (!m_restoreAllowed) { + if (mode == SyntheticLostContext) + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + int contextLostReason = m_context->getExtensions()->getGraphicsResetStatusARB(); + + switch (contextLostReason) { + case GraphicsContext3D::NO_ERROR: + // The GraphicsContext3D implementation might not fully + // support GL_ARB_robustness semantics yet. Alternatively, the + // WebGL WEBKIT_lose_context extension might have been used to + // force a lost context. + break; + case Extensions3D::GUILTY_CONTEXT_RESET_ARB: + // The rendering context is not restored if this context was + // guilty of causing the graphics reset. + printWarningToConsole("WARNING: WebGL content on the page caused the graphics card to reset; not restoring the context"); + return; + case Extensions3D::INNOCENT_CONTEXT_RESET_ARB: + // Always allow the context to be restored. + break; + case Extensions3D::UNKNOWN_CONTEXT_RESET_ARB: + // Warn. Ideally, prompt the user telling them that WebGL + // content on the page might have caused the graphics card to + // reset and ask them whether they want to continue running + // the content. Only if they say "yes" should we start + // attempting to restore the context. + printWarningToConsole("WARNING: WebGL content on the page might have caused the graphics card to reset"); + break; + } + +#if PLATFORM(ANDROID) + RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(canvas(), m_attributes, 0)); +#else RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(m_attributes, canvas()->document()->view()->root()->hostWindow())); - if (!context) +#endif + if (!context) { + if (mode == RealLostContext) + m_restoreTimer.startOneShot(secondsBetweenRestoreAttempts); + else + // This likely shouldn't happen but is the best way to report it to the WebGL app. + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; + } m_context = context; m_contextLost = false; @@ -3927,59 +4105,10 @@ void WebGLRenderingContext::addObject(WebGLObject* object) void WebGLRenderingContext::detachAndRemoveAllObjects() { - HashSet<RefPtr<WebGLObject> >::iterator pend = m_canvasObjects.end(); - for (HashSet<RefPtr<WebGLObject> >::iterator it = m_canvasObjects.begin(); it != pend; ++it) + while (m_canvasObjects.size() > 0) { + HashSet<WebGLObject*>::iterator it = m_canvasObjects.begin(); (*it)->detachContext(); - - m_canvasObjects.clear(); -} - -WebGLTexture* WebGLRenderingContext::findTexture(Platform3DObject obj) -{ - if (!obj) - return 0; - HashSet<RefPtr<WebGLObject> >::iterator pend = m_canvasObjects.end(); - for (HashSet<RefPtr<WebGLObject> >::iterator it = m_canvasObjects.begin(); it != pend; ++it) { - if ((*it)->isTexture() && (*it)->object() == obj) - return reinterpret_cast<WebGLTexture*>((*it).get()); } - return 0; -} - -WebGLRenderbuffer* WebGLRenderingContext::findRenderbuffer(Platform3DObject obj) -{ - if (!obj) - return 0; - HashSet<RefPtr<WebGLObject> >::iterator pend = m_canvasObjects.end(); - for (HashSet<RefPtr<WebGLObject> >::iterator it = m_canvasObjects.begin(); it != pend; ++it) { - if ((*it)->isRenderbuffer() && (*it)->object() == obj) - return reinterpret_cast<WebGLRenderbuffer*>((*it).get()); - } - return 0; -} - -WebGLBuffer* WebGLRenderingContext::findBuffer(Platform3DObject obj) -{ - if (!obj) - return 0; - HashSet<RefPtr<WebGLObject> >::iterator pend = m_canvasObjects.end(); - for (HashSet<RefPtr<WebGLObject> >::iterator it = m_canvasObjects.begin(); it != pend; ++it) { - if ((*it)->isBuffer() && (*it)->object() == obj) - return reinterpret_cast<WebGLBuffer*>((*it).get()); - } - return 0; -} - -WebGLShader* WebGLRenderingContext::findShader(Platform3DObject obj) -{ - if (!obj) - return 0; - HashSet<RefPtr<WebGLObject> >::iterator pend = m_canvasObjects.end(); - for (HashSet<RefPtr<WebGLObject> >::iterator it = m_canvasObjects.begin(); it != pend; ++it) { - if ((*it)->isShader() && (*it)->object() == obj) - return reinterpret_cast<WebGLShader*>((*it).get()); - } - return 0; } WebGLGetInfo WebGLRenderingContext::getBooleanParameter(GC3Denum pname) @@ -4198,6 +4327,16 @@ WebGLTexture* WebGLRenderingContext::validateTextureBinding(GC3Denum target, boo return tex; } +bool WebGLRenderingContext::validateLocationLength(const String& string) +{ + const unsigned maxWebGLLocationLength = 256; + if (string.length() > maxWebGLLocationLength) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return true; +} + bool WebGLRenderingContext::validateSize(GC3Dint x, GC3Dint y) { if (x < 0 || y < 0) { |