Bug 1077183 - Untangle the confusion between effective and unsized internalformats in the WebGL implementation - r=jgilbert
authorBenoit Jacob <bjacob@mozilla.com>
Tue, 07 Oct 2014 19:52:58 -0400
changeset 232460 97068dca449ef5b8afa16bc54975d6aab02ec168
parent 232459 e91da451577285cc36cf6cc2082a6563d6f7ac09
child 232461 c78b949b21da028939c2dac178e06fce5984cb8c
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1077183
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1077183 - Untangle the confusion between effective and unsized internalformats in the WebGL implementation - r=jgilbert
dom/canvas/WebGL2ContextTextures.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextUtils.h
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLInternalFormatsTable.h
dom/canvas/WebGLStrongTypes.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
gfx/gl/GLContext.cpp
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -148,26 +148,19 @@ WebGL2Context::TexStorage2D(GLenum targe
     WebGLTexture* tex = activeBoundTextureForTarget(target);
     tex->SetImmutable();
 
     const size_t facesCount = (target == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
     GLsizei w = width;
     GLsizei h = height;
     for (size_t l = 0; l < size_t(levels); l++) {
         for (size_t f = 0; f < facesCount; f++) {
-            TexImageTarget imageTarget = TexImageTargetForTargetAndFace(target, f);
-            // FIXME: SetImageInfo wants a type, to go with the internalformat that it stores.
-            // 'type' is deprecated by sized internalformats, which are how TexStorage works.
-            // We must fix WebGLTexture::ImageInfo to store an "effective internalformat",
-            // which in the present case is just the sized internalformat, and drop 'types'
-            // altogether. For now, we just pass LOCAL_GL_UNSIGNED_BYTE, which works For
-            // the most commonly used formats.
-            const GLenum type = LOCAL_GL_UNSIGNED_BYTE;
-            tex->SetImageInfo(imageTarget, l, w, h,
-                              internalformat, type,
+            tex->SetImageInfo(TexImageTargetForTargetAndFace(target, f),
+                              l, w, h,
+                              internalformat,
                               WebGLImageDataStatus::UninitializedImageData);
         }
         w = std::max(1, w/2);
         h = std::max(1, h/2);
     }
 
     gl->fTexStorage2D(target, levels, internalformat, width, height);
 }
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1755,16 +1755,23 @@ WebGLContext::DidRefresh()
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
 bool WebGLContext::TexImageFromVideoElement(const TexImageTarget texImageTarget, GLint level,
                               GLenum internalformat, GLenum format, GLenum type,
                               mozilla::dom::Element& elt)
 {
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        type = LOCAL_GL_HALF_FLOAT;
+    }
+
+    if (!ValidateTexImageFormatAndType(format, type, WebGLTexImageFunc::TexImage))
+        return false;
+
     HTMLVideoElement* video = HTMLVideoElement::FromContentOrNull(&elt);
     if (!video) {
         return false;
     }
 
     uint16_t readyState;
     if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
         readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)
@@ -1802,18 +1809,21 @@ bool WebGLContext::TexImageFromVideoElem
     bool dimensionsMatch = info.Width() == srcImage->GetSize().width &&
                            info.Height() == srcImage->GetSize().height;
     if (!dimensionsMatch) {
         // we need to allocation
         gl->fTexImage2D(texImageTarget.get(), level, internalformat, srcImage->GetSize().width, srcImage->GetSize().height, 0, format, type, nullptr);
     }
     bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage.get(), srcImage->GetSize(), tex->GLName(), texImageTarget.get(), mPixelStoreFlipY);
     if (ok) {
-        tex->SetImageInfo(texImageTarget, level, srcImage->GetSize().width, srcImage->GetSize().height, internalformat, type,
-                          WebGLImageDataStatus::InitializedImageData);
+        TexInternalFormat effectiveinternalformat =
+            EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
+        MOZ_ASSERT(effectiveinternalformat != LOCAL_GL_NONE);
+        tex->SetImageInfo(texImageTarget, level, srcImage->GetSize().width, srcImage->GetSize().height,
+                          effectiveinternalformat, WebGLImageDataStatus::InitializedImageData);
         tex->Bind(TexImageTargetToTexTarget(texImageTarget));
     }
     srcImage = nullptr;
     container->UnlockCurrentImage();
     return ok;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -89,17 +89,17 @@ class Element;
 struct WebGLContextAttributes;
 template<typename> struct Nullable;
 }
 
 namespace gfx {
 class SourceSurface;
 }
 
-WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format, TexType type);
+WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions {
     // these are defaults
     WebGLContextOptions();
 
     bool operator==(const WebGLContextOptions& other) const {
@@ -485,19 +485,16 @@ public:
 
         auto dims = 2;
 
         if (!ValidateTexImageTarget(dims, rawTexImgTarget, WebGLTexImageFunc::TexImage))
             return ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImgTarget);
 
         const TexImageTarget texImageTarget(rawTexImgTarget);
 
-        if (!ValidateTexImageFormatAndType(format, type, WebGLTexImageFunc::TexImage))
-            return;
-
         if (level < 0)
             return ErrorInvalidValue("texImage2D: level is negative");
 
         const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
         if (level > maxLevel)
             return ErrorInvalidValue("texImage2D: level %d is too large, max is %d", level, maxLevel);
 
         WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
@@ -555,32 +552,29 @@ public:
         if (IsContextLost())
             return;
 
         if (!ValidateTexImageTarget(2, rawTexImageTarget, WebGLTexImageFunc::TexSubImage))
             return ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
 
         const TexImageTarget texImageTarget(rawTexImageTarget);
 
-        if (!ValidateTexImageFormatAndType(format, type, WebGLTexImageFunc::TexImage))
-            return;
-
         if (level < 0)
             return ErrorInvalidValue("texSubImage2D: level is negative");
 
         const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
         if (level > maxLevel)
             return ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d", level, maxLevel);
 
         WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
         if (!tex) {
             return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
         }
         const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(texImageTarget, level);
-        const TexInternalFormat internalformat = imageInfo.InternalFormat();
+        const TexInternalFormat internalformat = imageInfo.EffectiveInternalFormat();
 
         // Trying to handle the video by GPU directly first
         if (TexImageFromVideoElement(texImageTarget, level,
                                      internalformat.get(), format, type, elt))
         {
             return;
         }
 
@@ -1140,18 +1134,16 @@ protected:
                                   GLsizei width, GLsizei height,
                                   GLsizei levelWidth, GLsizei levelHeight,
                                   WebGLTexImageFunc func);
     bool ValidateCompTexImageDataSize(GLint level,
                                       GLenum internalformat,
                                       GLsizei width, GLsizei height,
                                       uint32_t byteLength, WebGLTexImageFunc func);
 
-    static uint32_t GetBitsPerTexel(TexInternalFormat format, TexType type);
-
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
     // helpers
     void TexImage2D_base(TexImageTarget target,
                          GLint level,
@@ -1198,17 +1190,17 @@ protected:
     }
 
     nsresult SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
                                                     RefPtr<gfx::DataSourceSurface>& imageOut,
                                                     WebGLTexelFormat *format);
 
     void CopyTexSubImage2D_base(TexImageTarget texImageTarget,
                                 GLint level,
-                                GLenum internalformat,
+                                TexInternalFormat internalformat,
                                 GLint xoffset,
                                 GLint yoffset,
                                 GLint x,
                                 GLint y,
                                 GLsizei width,
                                 GLsizei height,
                                 bool sub);
 
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -671,17 +671,17 @@ WebGLContext::BindFakeBlackTexturesHelpe
         WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
         MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
 
         if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
             continue;
         }
 
         bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
-                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().InternalFormat());
+                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().EffectiveInternalFormat());
         UniquePtr<FakeBlackTexture>&
             blackTexturePtr = alpha
                               ? transparentTextureScopedPtr
                               : opaqueTextureScopedPtr;
 
         if (!blackTexturePtr) {
             GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
             blackTexturePtr = MakeUnique<FakeBlackTexture>(gl, target, format);
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -349,17 +349,17 @@ WebGLContext::CheckFramebufferStatus(GLe
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 
     return mBoundFramebuffer->CheckFramebufferStatus().get();
 }
 
 void
 WebGLContext::CopyTexSubImage2D_base(TexImageTarget texImageTarget,
                                      GLint level,
-                                     GLenum internalformat,
+                                     TexInternalFormat internalformat,
                                      GLint xoffset,
                                      GLint yoffset,
                                      GLint x,
                                      GLint y,
                                      GLsizei width,
                                      GLsizei height,
                                      bool sub)
 {
@@ -367,27 +367,27 @@ WebGLContext::CopyTexSubImage2D_base(Tex
     GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
     GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
 
     const char* info = sub ? "copyTexSubImage2D" : "copyTexImage2D";
     WebGLTexImageFunc func = sub ? WebGLTexImageFunc::CopyTexSubImage : WebGLTexImageFunc::CopyTexImage;
 
     // TODO: This changes with color_buffer_float. Reassess when the
     // patch lands.
-    if (!ValidateTexImage(2, texImageTarget, level, internalformat,
+    if (!ValidateTexImage(2, texImageTarget, level, internalformat.get(),
                           xoffset, yoffset, 0,
                           width, height, 0,
                           0,
                           LOCAL_GL_NONE, LOCAL_GL_NONE,
                           func))
     {
         return;
     }
 
-    if (!ValidateCopyTexImage(internalformat, func))
+    if (!ValidateCopyTexImage(internalformat.get(), func))
         return;
 
     if (!mBoundFramebuffer)
         ClearBackbufferIfNeeded();
 
     MakeContextCurrent();
 
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
@@ -396,60 +396,60 @@ WebGLContext::CopyTexSubImage2D_base(Tex
         return ErrorInvalidOperation("%s: no texture is bound to this target");
 
     if (tex->IsImmutable()) {
         if (!sub) {
             return ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D");
         }
     }
 
+    TexType framebuffertype = LOCAL_GL_NONE;
+    if (mBoundFramebuffer) {
+        TexInternalFormat framebuffereffectiveformat = mBoundFramebuffer->ColorAttachment(0).EffectiveInternalFormat();
+        framebuffertype = TypeFromInternalFormat(framebuffereffectiveformat);
+    } else {
+        // FIXME - here we're assuming that the default framebuffer is backed by UNSIGNED_BYTE
+        // that might not always be true, say if we had a 16bpp default framebuffer.
+        framebuffertype = LOCAL_GL_UNSIGNED_BYTE;
+    }
+
+    TexInternalFormat effectiveinternalformat =
+        EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, framebuffertype);
+
+    // this should never fail, validation happened earlier.
+    MOZ_ASSERT(effectiveinternalformat != LOCAL_GL_NONE);
+
+    // check if the memory size of this texture may change with this call
+    bool sizeMayChange = !sub;
+    if (!sub && tex->HasImageInfoAt(texImageTarget, level)) {
+        const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
+        sizeMayChange = width != imageInfo.Width() ||
+                        height != imageInfo.Height() ||
+                        effectiveinternalformat != imageInfo.EffectiveInternalFormat();
+    }
+
+    if (sizeMayChange)
+        GetAndFlushUnderlyingGLErrors();
+
     if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
         if (sub)
             gl->fCopyTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, x, y, width, height);
         else
-            gl->fCopyTexImage2D(texImageTarget.get(), level, internalformat, x, y, width, height, 0);
+            gl->fCopyTexImage2D(texImageTarget.get(), level, internalformat.get(), x, y, width, height, 0);
     } else {
 
         // the rect doesn't fit in the framebuffer
 
-        /*** first, we initialize the texture as black ***/
-
-        // first, compute the size of the buffer we should allocate to initialize the texture as black
-
-        if (!ValidateTexInputData(LOCAL_GL_UNSIGNED_BYTE, -1, func))
-            return;
-
-        uint32_t texelSize = GetBitsPerTexel(internalformat, LOCAL_GL_UNSIGNED_BYTE) / 8;
-
-        CheckedUint32 checked_neededByteLength =
-            GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
-
-        if (!checked_neededByteLength.isValid())
-            return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
-
-        uint32_t bytesNeeded = checked_neededByteLength.value();
-
-        // now that the size is known, create the buffer
-
-        // We need some zero pages, because GL doesn't guarantee the
-        // contents of a texture allocated with nullptr data.
-        // Hopefully calloc will just mmap zero pages here.
-        void* tempZeroData = calloc(1, bytesNeeded);
-        if (!tempZeroData)
-            return ErrorOutOfMemory("%s: could not allocate %d bytes (for zero fill)", info, bytesNeeded);
-
-        // now initialize the texture as black
-
-        if (sub)
-            gl->fTexSubImage2D(texImageTarget.get(), level, 0, 0, width, height,
-                               internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
-        else
-            gl->fTexImage2D(texImageTarget.get(), level, internalformat, width, height,
-                            0, internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
-        free(tempZeroData);
+        // first, we initialize the texture as black
+        if (!sub) {
+            tex->SetImageInfo(texImageTarget, level, width, height,
+                      effectiveinternalformat,
+                      WebGLImageDataStatus::UninitializedImageData);
+            tex->DoDeferredImageInitialization(texImageTarget, level);
+        }
 
         // if we are completely outside of the framebuffer, we can exit now with our black texture
         if (   x >= framebufferWidth
             || x+width <= 0
             || y >= framebufferHeight
             || y+height <= 0)
         {
             // we are completely outside of range, can exit now with buffer filled with zeros
@@ -463,16 +463,30 @@ WebGLContext::CopyTexSubImage2D_base(Tex
 
         GLint   actual_y             = clamped(y, 0, framebufferHeight);
         GLint   actual_y_plus_height = clamped(y + height, 0, framebufferHeight);
         GLsizei actual_height  = actual_y_plus_height - actual_y;
         GLint   actual_yoffset = yoffset + actual_y - y;
 
         gl->fCopyTexSubImage2D(texImageTarget.get(), level, actual_xoffset, actual_yoffset, actual_x, actual_y, actual_width, actual_height);
     }
+
+    if (sizeMayChange) {
+        GLenum error = GetAndFlushUnderlyingGLErrors();
+        if (error) {
+            GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
+            return;
+        }
+    }
+
+    if (!sub) {
+        tex->SetImageInfo(texImageTarget, level, width, height,
+                          effectiveinternalformat,
+                          WebGLImageDataStatus::InitializedImageData);
+    }
 }
 
 void
 WebGLContext::CopyTexImage2D(GLenum rawTexImgTarget,
                              GLint level,
                              GLenum internalformat,
                              GLint x,
                              GLint y,
@@ -499,47 +513,17 @@ WebGLContext::CopyTexImage2D(GLenum rawT
     }
 
     if (!ValidateCopyTexImage(internalformat, func))
         return;
 
     if (!mBoundFramebuffer)
         ClearBackbufferIfNeeded();
 
-    const TexImageTarget texImageTarget(rawTexImgTarget);
-
-    // check if the memory size of this texture may change with this call
-    bool sizeMayChange = true;
-    WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
-    if (tex->HasImageInfoAt(texImageTarget, level)) {
-        const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-
-        sizeMayChange = width != imageInfo.Width() ||
-                        height != imageInfo.Height() ||
-                        internalformat != imageInfo.InternalFormat();
-    }
-
-    if (sizeMayChange)
-        GetAndFlushUnderlyingGLErrors();
-
-    CopyTexSubImage2D_base(texImageTarget, level, internalformat, 0, 0, x, y, width, height, false);
-
-    if (sizeMayChange) {
-        GLenum error = GetAndFlushUnderlyingGLErrors();
-        if (error) {
-            GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
-            return;
-        }
-    }
-
-    tex->SetImageInfo(texImageTarget, level, width, height,
-                      internalformat,
-                      LOCAL_GL_UNSIGNED_BYTE, /* dummy, artifact of us storing
-                                               the wrong data in ImageInfo */
-                      WebGLImageDataStatus::InitializedImageData);
+    CopyTexSubImage2D_base(rawTexImgTarget, level, internalformat, 0, 0, x, y, width, height, false);
 }
 
 void
 WebGLContext::CopyTexSubImage2D(GLenum rawTexImgTarget,
                                 GLint level,
                                 GLint xoffset,
                                 GLint yoffset,
                                 GLint x,
@@ -597,17 +581,21 @@ WebGLContext::CopyTexSubImage2D(GLenum r
 
     if (!mBoundFramebuffer)
         ClearBackbufferIfNeeded();
 
     if (imageInfo.HasUninitializedImageData()) {
         tex->DoDeferredImageInitialization(texImageTarget, level);
     }
 
-    return CopyTexSubImage2D_base(texImageTarget, level, imageInfo.InternalFormat().get(), xoffset, yoffset, x, y, width, height, true);
+    TexInternalFormat internalformat;
+    TexType type;
+    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.EffectiveInternalFormat(),
+                                             &internalformat, &type);
+    return CopyTexSubImage2D_base(texImageTarget, level, internalformat, xoffset, yoffset, x, y, width, height, true);
 }
 
 
 already_AddRefed<WebGLProgram>
 WebGLContext::CreateProgram()
 {
     if (IsContextLost())
         return nullptr;
@@ -917,17 +905,17 @@ WebGLContext::GenerateMipmap(GLenum rawT
     if (!tex->HasImageInfoAt(imageTarget, 0))
     {
         return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
     }
 
     if (!tex->IsFirstImagePowerOfTwo())
         return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
 
-    TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).InternalFormat();
+    TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
     if (IsTextureFormatCompressed(internalformat))
         return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
         (IsGLDepthFormat(internalformat) || IsGLDepthStencilFormat(internalformat)))
     {
         return ErrorInvalidOperation("generateMipmap: "
                                      "A texture that has a base internal format of "
@@ -1178,22 +1166,27 @@ WebGLContext::GetFramebufferAttachmentPa
         }
 
         ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
         return JS::NullValue();
     } else if (fba.Texture()) {
         switch (pname) {
              case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
                 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
-                    const TexInternalFormat internalformat =
-                        fba.Texture()->ImageInfoBase().InternalFormat();
-                    return (internalformat == LOCAL_GL_SRGB ||
-                            internalformat == LOCAL_GL_SRGB_ALPHA) ?
-                        JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) :
-                        JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
+                    const TexInternalFormat effectiveinternalformat =
+                        fba.Texture()->ImageInfoBase().EffectiveInternalFormat();
+                    TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE;
+                    TexType type = LOCAL_GL_NONE;
+                    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(
+                        effectiveinternalformat, &unsizedinternalformat, &type);
+                    MOZ_ASSERT(unsizedinternalformat != LOCAL_GL_NONE);
+                    const bool srgb = unsizedinternalformat == LOCAL_GL_SRGB ||
+                                      unsizedinternalformat == LOCAL_GL_SRGB_ALPHA;
+                    return srgb ? JS::NumberValue(uint32_t(LOCAL_GL_SRGB))
+                                : JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
                 }
                 break;
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                 return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE));
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
                 return WebGLObjectAsJSValue(cx, fba.Texture(), rv);
@@ -1219,28 +1212,29 @@ WebGLContext::GetFramebufferAttachmentPa
                     ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot component"
                                           " type of depth-stencil attachments.");
                     return JS::NullValue();
                 }
 
                 if (!fba.IsComplete())
                     return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
 
-                uint32_t ret = LOCAL_GL_NONE;
-                TexType type = fba.Texture()->ImageInfoAt(fba.ImageTarget(),
-                                                          fba.MipLevel()).Type();
+                TexInternalFormat effectiveinternalformat =
+                    fba.Texture()->ImageInfoAt(fba.ImageTarget(), fba.MipLevel()).EffectiveInternalFormat();
+                TexType type = TypeFromInternalFormat(effectiveinternalformat);
+                GLenum ret = LOCAL_GL_NONE;
                 switch (type.get()) {
                 case LOCAL_GL_UNSIGNED_BYTE:
                 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
                 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
                 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
                     ret = LOCAL_GL_UNSIGNED_NORMALIZED;
                     break;
                 case LOCAL_GL_FLOAT:
-                case LOCAL_GL_HALF_FLOAT_OES:
+                case LOCAL_GL_HALF_FLOAT:
                     ret = LOCAL_GL_FLOAT;
                     break;
                 case LOCAL_GL_UNSIGNED_SHORT:
                 case LOCAL_GL_UNSIGNED_INT:
                     ret = LOCAL_GL_UNSIGNED_INT;
                     break;
                 default:
                     MOZ_ASSERT(false, "Unhandled RB component type.");
@@ -3348,17 +3342,17 @@ WebGLContext::CompressedTexImage2D(GLenu
         return ErrorInvalidOperation(
             "compressedTexImage2D: disallowed because the texture bound to "
             "this target has already been made immutable by texStorage2D");
     }
 
     MakeContextCurrent();
     gl->fCompressedTexImage2D(texImageTarget.get(), level, internalformat, width, height, border, byteLength, view.Data());
 
-    tex->SetImageInfo(texImageTarget, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
+    tex->SetImageInfo(texImageTarget, level, width, height, internalformat,
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CompressedTexSubImage2D(GLenum rawTexImgTarget, GLint level, GLint xoffset,
                                       GLint yoffset, GLsizei width, GLsizei height,
                                       GLenum internalformat,
                                       const ArrayBufferView& view)
@@ -3382,17 +3376,17 @@ WebGLContext::CompressedTexSubImage2D(GL
     }
 
     const TexImageTarget texImageTarget(rawTexImgTarget);
 
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
     MOZ_ASSERT(tex);
     WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(texImageTarget, level);
 
-    if (internalformat != levelInfo.InternalFormat()) {
+    if (internalformat != levelInfo.EffectiveInternalFormat()) {
         return ErrorInvalidOperation("compressedTexImage2D: internalformat does not match the existing image");
     }
 
     view.ComputeLengthAndData();
 
     uint32_t byteLength = view.Length();
     if (!ValidateCompTexImageDataSize(level, internalformat, width, height, byteLength, func))
         return;
@@ -3594,41 +3588,47 @@ WebGLContext::GetShaderTranslatedSource(
     if (!ValidateObject("getShaderTranslatedSource: shader", shader))
         return;
 
     retval.Assign(shader->TranslatedSource());
 }
 
 GLenum WebGLContext::CheckedTexImage2D(TexImageTarget texImageTarget,
                                        GLint level,
-                                       TexInternalFormat internalFormat,
+                                       TexInternalFormat internalformat,
                                        GLsizei width,
                                        GLsizei height,
                                        GLint border,
                                        TexFormat format,
                                        TexType type,
                                        const GLvoid *data)
 {
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
     MOZ_ASSERT(tex != nullptr, "no texture bound");
 
+    TexInternalFormat effectiveInternalFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
     bool sizeMayChange = true;
 
     if (tex->HasImageInfoAt(texImageTarget, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        internalFormat != imageInfo.InternalFormat();
+                        effectiveInternalFormat != imageInfo.EffectiveInternalFormat();
     }
 
     // Convert to format and type required by OpenGL 'driver'.
-    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverType = LOCAL_GL_NONE;
     GLenum driverInternalFormat = LOCAL_GL_NONE;
     GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromFormatAndType(gl, internalFormat, type, &driverInternalFormat, &driverFormat);
+    DriverFormatsFromEffectiveInternalFormat(gl,
+                                             effectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverFormat,
+                                             &driverType);
 
     if (sizeMayChange) {
         GetAndFlushUnderlyingGLErrors();
     }
 
     gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
 
     GLenum error = LOCAL_GL_NO_ERROR;
@@ -3647,16 +3647,20 @@ WebGLContext::TexImage2D_base(TexImageTa
                               GLenum format,
                               GLenum type,
                               void* data, uint32_t byteLength,
                               int jsArrayType, // a TypedArray format enum, or -1 if not relevant
                               WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
 
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        type = LOCAL_GL_HALF_FLOAT;
+    }
+
     if (!ValidateTexImage(2, texImageTarget, level, internalformat,
                           0, 0, 0,
                           width, height, 0,
                           border, format, type, func))
     {
         return;
     }
 
@@ -3669,17 +3673,24 @@ WebGLContext::TexImage2D_base(TexImageTa
                                          "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
                                          "data must be nullptr, "
                                          "level must be zero");
     }
 
     if (!ValidateTexInputData(type, jsArrayType, func))
         return;
 
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(internalformat, type);
+    TexInternalFormat effectiveinternalformat =
+        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
+
+    if (effectiveinternalformat == LOCAL_GL_NONE) {
+        return ErrorInvalidOperation("texImage2D: bad combination of internalformat and type");
+    }
+
+    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(effectiveinternalformat);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
@@ -3707,18 +3718,20 @@ WebGLContext::TexImage2D_base(TexImageTa
     }
     MakeContextCurrent();
 
     nsAutoArrayPtr<uint8_t> convertedData;
     void* pixels = nullptr;
     WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
 
     if (byteLength) {
+        size_t   bitspertexel = GetBitsPerTexel(effectiveinternalformat);
+        MOZ_ASSERT((bitspertexel % 8) == 0); // should not have compressed formats here.
+        size_t   dstTexelSize = bitspertexel / 8;
         size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-        uint32_t dstTexelSize = GetBitsPerTexel(internalformat, type) / 8;
         size_t   dstPlainRowSize = dstTexelSize * width;
         size_t   unpackAlignment = mPixelStoreUnpackAlignment;
         size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
 
         if (actualSrcFormat == dstFormat &&
             srcPremultiplied == mPixelStorePremultiplyAlpha &&
             srcStride == dstStride &&
             !mPixelStoreFlipY)
@@ -3747,17 +3760,18 @@ WebGLContext::TexImage2D_base(TexImageTa
         return;
     }
 
     // in all of the code paths above, we should have either initialized data,
     // or allocated data and left it uninitialized, but in any case we shouldn't
     // have NoImageData at this point.
     MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
 
-    tex->SetImageInfo(texImageTarget, level, width, height, internalformat, type, imageInfoStatusIfSuccess);
+    tex->SetImageInfo(texImageTarget, level, width, height,
+                      effectiveinternalformat, imageInfoStatusIfSuccess);
 }
 
 void
 WebGLContext::TexImage2D(GLenum rawTarget, GLint level,
                          GLenum internalformat, GLsizei width,
                          GLsizei height, GLint border, GLenum format,
                          GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
 {
@@ -3825,39 +3839,53 @@ WebGLContext::TexSubImage2D_base(TexImag
                                  GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                                  GLenum format, GLenum type,
                                  void* data, uint32_t byteLength,
                                  int jsArrayType,
                                  WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
 
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        type = LOCAL_GL_HALF_FLOAT;
+    }
+
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
     if (!tex) {
         return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
     }
+
+    if (!tex->HasImageInfoAt(texImageTarget, level)) {
+        return ErrorInvalidOperation("texSubImage2D: no previously defined texture image");
+    }
+
     const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-    const TexInternalFormat internalformat =  imageInfo.InternalFormat();
-
-    if (!ValidateTexImage(2, texImageTarget, level, internalformat.get(),
+    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat();
+    TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE;
+    TexType existingType = LOCAL_GL_NONE;
+    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat,
+                                                            &existingUnsizedInternalFormat,
+                                                            &existingType);
+
+    if (!ValidateTexImage(2, texImageTarget, level, existingUnsizedInternalFormat.get(),
                           xoffset, yoffset, 0,
                           width, height, 0,
                           0, format, type, func))
     {
         return;
     }
 
     if (!ValidateTexInputData(type, jsArrayType, func))
         return;
 
-    if (imageInfo.Type() != type) {
-        return ErrorInvalidOperation("texSubImage2D: type parameter does not match the existing image");
+    if (type != existingType) {
+        return ErrorInvalidOperation("texSubImage2D: type differs from that of the existing image");
     }
 
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(internalformat, type);
+    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(existingEffectiveInternalFormat);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     if (width == 0 || height == 0)
         return; // ES 2.0 says it has no effect, we better return right now
 
     CheckedUint32 checked_neededByteLength =
@@ -3877,17 +3905,17 @@ WebGLContext::TexSubImage2D_base(TexImag
         return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
 
     if (imageInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(texImageTarget, level);
 
     MakeContextCurrent();
 
     size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-    uint32_t dstTexelSize = GetBitsPerTexel(internalformat, type) / 8;
+    uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8;
     size_t   dstPlainRowSize = dstTexelSize * width;
     // There are checks above to ensure that this won't overflow.
     size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
 
     void* pixels = data;
     nsAutoArrayPtr<uint8_t> convertedData;
 
     // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
@@ -3901,20 +3929,24 @@ WebGLContext::TexSubImage2D_base(TexImag
         convertedData = new uint8_t[convertedDataSize];
         ConvertImage(width, height, srcStride, dstStride,
                     static_cast<const uint8_t*>(data), convertedData,
                     actualSrcFormat, srcPremultiplied,
                     dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
         pixels = reinterpret_cast<void*>(convertedData.get());
     }
 
-    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverType = LOCAL_GL_NONE;
     GLenum driverInternalFormat = LOCAL_GL_NONE;
     GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromFormatAndType(gl, internalformat, type, &driverInternalFormat, &driverFormat);
+    DriverFormatsFromEffectiveInternalFormat(gl,
+                                             existingEffectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverFormat,
+                                             &driverType);
 
     gl->fTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum rawTarget, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height,
@@ -4061,129 +4093,47 @@ BaseTypeAndSizeFromUniformType(GLenum uT
         default:
             return false;
     }
 
     return true;
 }
 
 
-WebGLTexelFormat mozilla::GetWebGLTexelFormat(TexInternalFormat internalformat, TexType type)
+WebGLTexelFormat
+mozilla::GetWebGLTexelFormat(TexInternalFormat effectiveinternalformat)
 {
-    //
-    // WEBGL_depth_texture
-    if (internalformat == LOCAL_GL_DEPTH_COMPONENT) {
-        switch (type.get()) {
-            case LOCAL_GL_UNSIGNED_SHORT:
-                return WebGLTexelFormat::D16;
-            case LOCAL_GL_UNSIGNED_INT:
-                return WebGLTexelFormat::D32;
-        }
-
-        MOZ_CRASH("Invalid WebGL texture format/type?");
-    }
-
-    if (internalformat == LOCAL_GL_DEPTH_STENCIL) {
-        switch (type.get()) {
-            case LOCAL_GL_UNSIGNED_INT_24_8_EXT:
-                return WebGLTexelFormat::D24S8;
-        }
-
-        MOZ_CRASH("Invalid WebGL texture format/type?");
-    }
-
-    if (internalformat == LOCAL_GL_DEPTH_COMPONENT16) {
-        return WebGLTexelFormat::D16;
-    }
-
-    if (internalformat == LOCAL_GL_DEPTH_COMPONENT32) {
-        return WebGLTexelFormat::D32;
-    }
-
-    if (internalformat == LOCAL_GL_DEPTH24_STENCIL8) {
-        return WebGLTexelFormat::D24S8;
-    }
-
-    if (type == LOCAL_GL_UNSIGNED_BYTE) {
-        switch (internalformat.get()) {
-            case LOCAL_GL_RGBA:
-            case LOCAL_GL_SRGB_ALPHA_EXT:
-                return WebGLTexelFormat::RGBA8;
-            case LOCAL_GL_RGB:
-            case LOCAL_GL_SRGB_EXT:
-                return WebGLTexelFormat::RGB8;
-            case LOCAL_GL_ALPHA:
-                return WebGLTexelFormat::A8;
-            case LOCAL_GL_LUMINANCE:
-                return WebGLTexelFormat::R8;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                return WebGLTexelFormat::RA8;
-        }
-
-        MOZ_CRASH("Invalid WebGL texture format/type?");
-    }
-
-    if (type == LOCAL_GL_FLOAT) {
-        // OES_texture_float
-        switch (internalformat.get()) {
-            case LOCAL_GL_RGBA:
-            case LOCAL_GL_RGBA32F:
-                return WebGLTexelFormat::RGBA32F;
-            case LOCAL_GL_RGB:
-            case LOCAL_GL_RGB32F:
-                return WebGLTexelFormat::RGB32F;
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_ALPHA32F_ARB:
-                return WebGLTexelFormat::A32F;
-            case LOCAL_GL_LUMINANCE:
-            case LOCAL_GL_LUMINANCE32F_ARB:
-                return WebGLTexelFormat::R32F;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-            case LOCAL_GL_LUMINANCE_ALPHA32F_ARB:
-                return WebGLTexelFormat::RA32F;
-        }
-
-        MOZ_CRASH("Invalid WebGL texture format/type?");
-    } else if (type == LOCAL_GL_HALF_FLOAT_OES) {
-        // OES_texture_half_float
-        switch (internalformat.get()) {
-            case LOCAL_GL_RGBA:
-            case LOCAL_GL_RGBA16F:
-                return WebGLTexelFormat::RGBA16F;
-            case LOCAL_GL_RGB:
-            case LOCAL_GL_RGB16F:
-                return WebGLTexelFormat::RGB16F;
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_ALPHA16F_ARB:
-                return WebGLTexelFormat::A16F;
-            case LOCAL_GL_LUMINANCE:
-            case LOCAL_GL_LUMINANCE16F_ARB:
-                return WebGLTexelFormat::R16F;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-            case LOCAL_GL_LUMINANCE_ALPHA16F_ARB:
-                return WebGLTexelFormat::RA16F;
-            default:
-                MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
-                return WebGLTexelFormat::BadFormat;
-        }
-    }
-
-    switch (type.get()) {
-        case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-           return WebGLTexelFormat::RGBA4444;
-        case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-           return WebGLTexelFormat::RGBA5551;
-        case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-           return WebGLTexelFormat::RGB565;
+    switch (effectiveinternalformat.get()) {
+        case LOCAL_GL_DEPTH_COMPONENT16:      return WebGLTexelFormat::D16;
+        case LOCAL_GL_DEPTH_COMPONENT24:      return WebGLTexelFormat::D32;
+        case LOCAL_GL_DEPTH24_STENCIL8:       return WebGLTexelFormat::D24S8;
+        case LOCAL_GL_RGBA8:                  return WebGLTexelFormat::RGBA8;
+        case LOCAL_GL_SRGB8_ALPHA8:           return WebGLTexelFormat::RGBA8;
+        case LOCAL_GL_RGB8:                   return WebGLTexelFormat::RGB8;
+        case LOCAL_GL_SRGB8:                  return WebGLTexelFormat::RGB8;
+        case LOCAL_GL_ALPHA8:                 return WebGLTexelFormat::A8;
+        case LOCAL_GL_LUMINANCE8:             return WebGLTexelFormat::R8;
+        case LOCAL_GL_LUMINANCE8_ALPHA8:      return WebGLTexelFormat::RA8;
+        case LOCAL_GL_RGBA32F:                return WebGLTexelFormat::RGBA32F;
+        case LOCAL_GL_RGB32F:                 return WebGLTexelFormat::RGB32F;
+        case LOCAL_GL_ALPHA32F_EXT:           return WebGLTexelFormat::A32F;
+        case LOCAL_GL_LUMINANCE32F_EXT:       return WebGLTexelFormat::R32F;
+        case LOCAL_GL_LUMINANCE_ALPHA32F_EXT: return WebGLTexelFormat::RA32F;
+        case LOCAL_GL_RGBA16F:                return WebGLTexelFormat::RGBA16F;
+        case LOCAL_GL_RGB16F:                 return WebGLTexelFormat::RGB16F;
+        case LOCAL_GL_ALPHA16F_EXT:           return WebGLTexelFormat::A16F;
+        case LOCAL_GL_LUMINANCE16F_EXT:       return WebGLTexelFormat::R16F;
+        case LOCAL_GL_LUMINANCE_ALPHA16F_EXT: return WebGLTexelFormat::RA16F;
+        case LOCAL_GL_RGBA4:                  return WebGLTexelFormat::RGBA4444;
+        case LOCAL_GL_RGB5_A1:                return WebGLTexelFormat::RGBA5551;
+        case LOCAL_GL_RGB565:                 return WebGLTexelFormat::RGB565;
         default:
-            MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
+            MOZ_CRASH("Unhandled format");
             return WebGLTexelFormat::BadFormat;
     }
-
-    MOZ_CRASH("Invalid WebGL texture format/type?");
 }
 
 void
 WebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) {
     if (IsContextLost())
         return;
     MakeContextCurrent();
     gl->fBlendColor(r, g, b, a);
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -26,39 +26,38 @@
 
 #include "mozilla/dom/ScriptSettings.h"
 
 namespace mozilla {
 
 using namespace gl;
 
 bool
-IsGLDepthFormat(TexInternalFormat webGLFormat)
+IsGLDepthFormat(TexInternalFormat internalformat)
 {
-    return (webGLFormat == LOCAL_GL_DEPTH_COMPONENT ||
-            webGLFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
-            webGLFormat == LOCAL_GL_DEPTH_COMPONENT32);
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_DEPTH_COMPONENT;
 }
 
 bool
-IsGLDepthStencilFormat(TexInternalFormat webGLFormat)
+IsGLDepthStencilFormat(TexInternalFormat internalformat)
 {
-    return (webGLFormat == LOCAL_GL_DEPTH_STENCIL ||
-            webGLFormat == LOCAL_GL_DEPTH24_STENCIL8);
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_DEPTH_STENCIL;
 }
 
 bool
-FormatHasAlpha(TexInternalFormat webGLFormat)
+FormatHasAlpha(TexInternalFormat internalformat)
 {
-    return webGLFormat == LOCAL_GL_RGBA ||
-           webGLFormat == LOCAL_GL_LUMINANCE_ALPHA ||
-           webGLFormat == LOCAL_GL_ALPHA ||
-           webGLFormat == LOCAL_GL_RGBA4 ||
-           webGLFormat == LOCAL_GL_RGB5_A1 ||
-           webGLFormat == LOCAL_GL_SRGB_ALPHA;
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_RGBA ||
+           unsizedformat == LOCAL_GL_LUMINANCE_ALPHA ||
+           unsizedformat == LOCAL_GL_ALPHA ||
+           unsizedformat == LOCAL_GL_SRGB_ALPHA ||
+           unsizedformat == LOCAL_GL_RGBA_INTEGER;
 }
 
 TexTarget
 TexImageTargetToTexTarget(TexImageTarget texImageTarget)
 {
     switch (texImageTarget.get()) {
     case LOCAL_GL_TEXTURE_2D:
         return LOCAL_GL_TEXTURE_2D;
@@ -71,21 +70,22 @@ TexImageTargetToTexTarget(TexImageTarget
         return LOCAL_GL_TEXTURE_CUBE_MAP;
     default:
         MOZ_ASSERT(false, "Bad texture conversion");
         // Should be caught by the constructor for TexTarget
         return LOCAL_GL_NONE;
     }
 }
 
-GLComponents::GLComponents(TexInternalFormat format)
+GLComponents::GLComponents(TexInternalFormat internalformat)
 {
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
     mComponents = 0;
 
-    switch (format.get()) {
+    switch (unsizedformat.get()) {
         case LOCAL_GL_RGBA:
         case LOCAL_GL_RGBA4:
         case LOCAL_GL_RGBA8:
         case LOCAL_GL_RGB5_A1:
         // Luminance + Alpha can be converted
         // to and from RGBA
         case LOCAL_GL_LUMINANCE_ALPHA:
             mComponents |= Components::Alpha;
@@ -112,162 +112,287 @@ GLComponents::GLComponents(TexInternalFo
 }
 
 bool
 GLComponents::IsSubsetOf(const GLComponents& other) const
 {
     return (mComponents | other.mComponents) == other.mComponents;
 }
 
+TexType
+TypeFromInternalFormat(TexInternalFormat internalformat)
+{
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    if (internalformat == table_effectiveinternalformat) { \
+        return table_type; \
+    }
+
+#include "WebGLInternalFormatsTable.h"
+
+    // if we're here, then internalformat is not an effective internalformat i.e. is an unsized internalformat.
+    return LOCAL_GL_NONE; // no size, no type
+}
+
+TexInternalFormat
+UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat)
+{
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    if (internalformat == table_effectiveinternalformat) { \
+        return table_internalformat; \
+    }
+
+#include "WebGLInternalFormatsTable.h"
+
+    // if we're here, then internalformat is not an effective internalformat i.e. is an unsized internalformat.
+    // so we can just return it.
+    return internalformat;
+}
+
+/*
+ * Note that the following two functions are inverse of each other:
+ * EffectiveInternalFormatFromInternalFormatAndType and
+ * InternalFormatAndTypeFromEffectiveInternalFormat both implement OpenGL ES 3.0.3 Table 3.2
+ * but in opposite directions.
+ */
+TexInternalFormat
+EffectiveInternalFormatFromUnsizedInternalFormatAndType(TexInternalFormat internalformat,
+                                                        TexType type)
+{
+    MOZ_ASSERT(TypeFromInternalFormat(internalformat) == LOCAL_GL_NONE);
+
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    if (internalformat == table_internalformat && type == table_type) { \
+        return table_effectiveinternalformat; \
+    }
+
+#include "WebGLInternalFormatsTable.h"
+
+    // If we're here, that means that type was incompatible with the given internalformat.
+    return LOCAL_GL_NONE;
+}
+
+void
+UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat,
+                                                        TexInternalFormat* out_internalformat,
+                                                        TexType* out_type)
+{
+    MOZ_ASSERT(TypeFromInternalFormat(effectiveinternalformat) != LOCAL_GL_NONE);
+
+    MOZ_ASSERT(out_internalformat);
+    MOZ_ASSERT(out_type);
+
+    GLenum internalformat = LOCAL_GL_NONE;
+    GLenum type = LOCAL_GL_NONE;
+
+    switch (effectiveinternalformat.get()) {
+
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    case table_effectiveinternalformat: \
+        internalformat = table_internalformat; \
+        type = table_type; \
+        break;
+
+#include "WebGLInternalFormatsTable.h"
+
+        default:
+            MOZ_CRASH(); // impossible to get here
+    }
+
+    *out_internalformat = internalformat;
+    *out_type = type;
+}
+
+TexInternalFormat
+EffectiveInternalFormatFromInternalFormatAndType(TexInternalFormat internalformat,
+                                                 TexType type)
+{
+    TexType typeOfInternalFormat = TypeFromInternalFormat(internalformat);
+    if (typeOfInternalFormat == LOCAL_GL_NONE) {
+        return EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, type);
+    } else if (typeOfInternalFormat == type) {
+        return internalformat;
+    } else {
+        return LOCAL_GL_NONE;
+    }
+}
+
 /**
- * Convert WebGL/ES format and type into GL internal
- * format valid for underlying driver.
+ * Convert effective internalformat into GL function parameters
+ * valid for underlying driver.
  */
 void
-DriverFormatsFromFormatAndType(GLContext* gl, TexInternalFormat webGLInternalFormat, TexType webGLType,
-                               GLenum* out_driverInternalFormat, GLenum* out_driverFormat)
+DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl,
+                                         TexInternalFormat effectiveinternalformat,
+                                         GLenum* out_driverInternalFormat,
+                                         GLenum* out_driverFormat,
+                                         GLenum* out_driverType)
 {
     MOZ_ASSERT(out_driverInternalFormat);
     MOZ_ASSERT(out_driverFormat);
+    MOZ_ASSERT(out_driverType);
 
-    // ES2 requires that format == internalformat; floating-point is
-    // indicated purely by the type that's loaded.  For desktop GL, we
-    // have to specify a floating point internal format.
-    if (gl->IsGLES()) {
-        *out_driverFormat = *out_driverInternalFormat = webGLInternalFormat.get();
-        return;
+    TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE;
+    TexType type = LOCAL_GL_NONE;
+
+    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(effectiveinternalformat,
+                                                            &unsizedinternalformat, &type);
+
+    // driverType: almost always the generic type that we just got, except on ES
+    // we must replace HALF_FLOAT by HALF_FLOAT_OES
+    GLenum driverType = type.get();
+    if (gl->IsGLES() && type == LOCAL_GL_HALF_FLOAT) {
+        driverType = LOCAL_GL_HALF_FLOAT_OES;
     }
 
-    GLenum internalFormat = LOCAL_GL_NONE;
-    GLenum format = LOCAL_GL_NONE;
-
-    if (webGLInternalFormat == LOCAL_GL_DEPTH_COMPONENT) {
-        format = LOCAL_GL_DEPTH_COMPONENT;
-        if (webGLType == LOCAL_GL_UNSIGNED_SHORT)
-            internalFormat = LOCAL_GL_DEPTH_COMPONENT16;
-        else if (webGLType == LOCAL_GL_UNSIGNED_INT)
-            internalFormat = LOCAL_GL_DEPTH_COMPONENT32;
-    } else if (webGLInternalFormat == LOCAL_GL_DEPTH_STENCIL) {
-        format = LOCAL_GL_DEPTH_STENCIL;
-        if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            internalFormat = LOCAL_GL_DEPTH24_STENCIL8;
-    } else {
-        switch (webGLType.get()) {
-        case LOCAL_GL_UNSIGNED_BYTE:
-        case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-        case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-        case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-            format = internalFormat = webGLInternalFormat.get();
-            break;
-
-        case LOCAL_GL_FLOAT:
-            switch (webGLInternalFormat.get()) {
-            case LOCAL_GL_RGBA:
-                format = LOCAL_GL_RGBA;
-                internalFormat = LOCAL_GL_RGBA32F;
-                break;
-
-            case LOCAL_GL_RGB:
-                format = LOCAL_GL_RGB;
-                internalFormat = LOCAL_GL_RGB32F;
-                break;
-
-            case LOCAL_GL_ALPHA:
-                format = LOCAL_GL_ALPHA;
-                internalFormat = LOCAL_GL_ALPHA32F_ARB;
-                break;
+    // driverFormat: always just the unsized internalformat that we just got
+    GLenum driverFormat = unsizedinternalformat.get();
 
-            case LOCAL_GL_LUMINANCE:
-                format = LOCAL_GL_LUMINANCE;
-                internalFormat = LOCAL_GL_LUMINANCE32F_ARB;
-                break;
-
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                format = LOCAL_GL_LUMINANCE_ALPHA;
-                internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
-                break;
-            }
-            break;
-
-        case LOCAL_GL_HALF_FLOAT_OES:
-            switch (webGLInternalFormat.get()) {
-            case LOCAL_GL_RGBA:
-                format = LOCAL_GL_RGBA;
-                internalFormat = LOCAL_GL_RGBA16F;
-                break;
-
-            case LOCAL_GL_RGB:
-                format = LOCAL_GL_RGB;
-                internalFormat = LOCAL_GL_RGB16F;
-                break;
-
-            case LOCAL_GL_ALPHA:
-                format = LOCAL_GL_ALPHA;
-                internalFormat = LOCAL_GL_ALPHA16F_ARB;
-                break;
-
-            case LOCAL_GL_LUMINANCE:
-                format = LOCAL_GL_LUMINANCE;
-                internalFormat = LOCAL_GL_LUMINANCE16F_ARB;
-                break;
-
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                format = LOCAL_GL_LUMINANCE_ALPHA;
-                internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
-                break;
-            }
-            break;
-
-        default:
-            break;
+    // driverInternalFormat: almost always the same as driverFormat, but on desktop GL,
+    // in some cases we must pass a different value. On ES, they are equal by definition
+    // as it is an error to pass internalformat!=format.
+    GLenum driverInternalFormat = driverFormat;
+    if (!gl->IsGLES()) {
+        // Cases where desktop OpenGL requires a tweak to 'format'
+        if (driverFormat == LOCAL_GL_SRGB) {
+            driverFormat = LOCAL_GL_RGB;
+        } else if (driverFormat == LOCAL_GL_SRGB_ALPHA) {
+            driverFormat = LOCAL_GL_RGBA;
         }
 
-        // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES
-        // requires that format == internalformat, but GL will fail in this case.
-        // GL requires:
-        //      format  ->  internalformat
-        //      GL_RGB      GL_SRGB_EXT
-        //      GL_RGBA     GL_SRGB_ALPHA_EXT
-        switch (webGLInternalFormat.get()) {
-        case LOCAL_GL_SRGB:
-            format = LOCAL_GL_RGB;
-            internalFormat = LOCAL_GL_SRGB;
-            break;
-        case LOCAL_GL_SRGB_ALPHA:
-            format = LOCAL_GL_RGBA;
-            internalFormat = LOCAL_GL_SRGB_ALPHA;
-            break;
+        // Cases where desktop OpenGL requires a sized internalformat,
+        // as opposed to the unsized internalformat that had the same
+        // GLenum value as 'format', in order to get the precise
+        // semantics that we want. For example, for floating-point formats,
+        // we seem to need a sized internalformat to get non-clamped floating
+        // point texture sampling. Can't find the spec reference for that,
+        // but that's at least the case on my NVIDIA driver version 331.
+        if (unsizedinternalformat == LOCAL_GL_DEPTH_COMPONENT ||
+            unsizedinternalformat == LOCAL_GL_DEPTH_STENCIL ||
+            type == LOCAL_GL_FLOAT ||
+            type == LOCAL_GL_HALF_FLOAT)
+        {
+            driverInternalFormat = effectiveinternalformat.get();
         }
     }
 
-    MOZ_ASSERT(webGLInternalFormat != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE,
-               "Coding mistake -- bad format/type passed?");
-
-    *out_driverInternalFormat = internalFormat;
-    *out_driverFormat = format;
+    *out_driverInternalFormat = driverInternalFormat;
+    *out_driverFormat = driverFormat;
+    *out_driverType = driverType;
 }
 
-GLenum
-DriverTypeFromType(GLContext* gl, TexType webGLType)
+/**
+ * Return the bits per texel for format & type combination.
+ * Assumes that format & type are a valid combination as checked with
+ * ValidateTexImageFormatAndType().
+ */
+size_t
+GetBitsPerTexel(TexInternalFormat effectiveinternalformat)
 {
-    GLenum type = webGLType.get();
+    switch (effectiveinternalformat.get()) {
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
+        return 2;
+
+    case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case LOCAL_GL_ATC_RGB:
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
+    case LOCAL_GL_ETC1_RGB8_OES:
+        return 4;
 
-    if (gl->IsGLES())
-        return type;
+    case LOCAL_GL_ALPHA8:
+    case LOCAL_GL_LUMINANCE8:
+    case LOCAL_GL_R8:
+    case LOCAL_GL_R8I:
+    case LOCAL_GL_R8UI:
+    case LOCAL_GL_R8_SNORM:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+    case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
+    case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
+        return 8;
+
+    case LOCAL_GL_LUMINANCE8_ALPHA8:
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB5_A1:
+    case LOCAL_GL_DEPTH_COMPONENT16:
+    case LOCAL_GL_RG8:
+    case LOCAL_GL_R16I:
+    case LOCAL_GL_R16UI:
+    case LOCAL_GL_RGB565:
+    case LOCAL_GL_R16F:
+    case LOCAL_GL_RG8I:
+    case LOCAL_GL_RG8UI:
+    case LOCAL_GL_RG8_SNORM:
+    case LOCAL_GL_ALPHA16F_EXT:
+    case LOCAL_GL_LUMINANCE16F_EXT:
+        return 16;
 
-    // convert type for half float if not on GLES2
-    if (type == LOCAL_GL_HALF_FLOAT_OES) {
-        if (gl->IsSupported(gl::GLFeature::texture_half_float)) {
-            return LOCAL_GL_HALF_FLOAT;
-        } else {
-            MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
-        }
+    case LOCAL_GL_RGB8:
+    case LOCAL_GL_DEPTH_COMPONENT24:
+    case LOCAL_GL_SRGB8:
+    case LOCAL_GL_RGB8UI:
+    case LOCAL_GL_RGB8I:
+    case LOCAL_GL_RGB8_SNORM:
+        return 24;
+
+    case LOCAL_GL_RGBA8:
+    case LOCAL_GL_RGB10_A2:
+    case LOCAL_GL_R32F:
+    case LOCAL_GL_RG16F:
+    case LOCAL_GL_R32I:
+    case LOCAL_GL_R32UI:
+    case LOCAL_GL_RG16I:
+    case LOCAL_GL_RG16UI:
+    case LOCAL_GL_DEPTH24_STENCIL8:
+    case LOCAL_GL_R11F_G11F_B10F:
+    case LOCAL_GL_RGB9_E5:
+    case LOCAL_GL_SRGB8_ALPHA8:
+    case LOCAL_GL_DEPTH_COMPONENT32F:
+    case LOCAL_GL_RGBA8UI:
+    case LOCAL_GL_RGBA8I:
+    case LOCAL_GL_RGBA8_SNORM:
+    case LOCAL_GL_RGB10_A2UI:
+    case LOCAL_GL_LUMINANCE_ALPHA16F_EXT:
+    case LOCAL_GL_ALPHA32F_EXT:
+    case LOCAL_GL_LUMINANCE32F_EXT:
+        return 32;
+
+    case LOCAL_GL_DEPTH32F_STENCIL8:
+        return 40;
+
+    case LOCAL_GL_RGB16F:
+    case LOCAL_GL_RGB16UI:
+    case LOCAL_GL_RGB16I:
+        return 48;
+
+    case LOCAL_GL_RG32F:
+    case LOCAL_GL_RG32I:
+    case LOCAL_GL_RG32UI:
+    case LOCAL_GL_RGBA16F:
+    case LOCAL_GL_RGBA16UI:
+    case LOCAL_GL_RGBA16I:
+    case LOCAL_GL_LUMINANCE_ALPHA32F_EXT:
+        return 64;
+
+    case LOCAL_GL_RGB32F:
+    case LOCAL_GL_RGB32UI:
+    case LOCAL_GL_RGB32I:
+        return 96;
+
+    case LOCAL_GL_RGBA32F:
+    case LOCAL_GL_RGBA32UI:
+    case LOCAL_GL_RGBA32I:
+        return 128;
+
+    default:
+        MOZ_ASSERT(false, "Unhandled format");
+        return 0;
     }
-
-    return type;
 }
 
 void
 WebGLContext::GenerateWarning(const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
 
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -11,19 +11,38 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 
 namespace mozilla {
 
 bool IsGLDepthFormat(TexInternalFormat webGLFormat);
 bool IsGLDepthStencilFormat(TexInternalFormat webGLFormat);
 bool FormatHasAlpha(TexInternalFormat webGLFormat);
-void DriverFormatsFromFormatAndType(gl::GLContext* gl, TexInternalFormat webGLFormat, TexType webGLType,
-                                    GLenum* out_driverInternalFormat, GLenum* out_driverFormat);
-GLenum DriverTypeFromType(gl::GLContext* gl, TexType webGLType);
+void
+DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl,
+                                         TexInternalFormat internalformat,
+                                         GLenum* out_driverInternalFormat,
+                                         GLenum* out_driverFormat,
+                                         GLenum* out_driverType);
+TexInternalFormat
+EffectiveInternalFormatFromInternalFormatAndType(TexInternalFormat internalformat,
+                                                 TexType type);
+TexInternalFormat
+EffectiveInternalFormatFromUnsizedInternalFormatAndType(TexInternalFormat internalformat,
+                                                        TexType type);
+void
+UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat,
+                                                        TexInternalFormat* out_internalformat,
+                                                        TexType* out_type);
+TexType
+TypeFromInternalFormat(TexInternalFormat internalformat);
+TexInternalFormat
+UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat);
+size_t
+GetBitsPerTexel(TexInternalFormat effectiveinternalformat);
 
 // For use with the different texture calls, i.e.
 //   TexImage2D, CopyTex[Sub]Image2D, ...
 // that take a "target" parameter. This parameter is not always the same as
 // the texture binding location, like GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
 // For example, cube maps would pass GL_TEXTURE_CUBE_MAP_[POS|NEG]_[X|Y|Z]
 // instead of just GL_TEXTURE_CUBE_MAP.
 //
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -668,17 +668,17 @@ WebGLContext::ValidateTexImageType(GLenu
         bool validType = IsExtensionEnabled(WebGLExtensionID::OES_texture_float);
         if (!validType)
             ErrorInvalidEnum("%s: invalid type %s: need OES_texture_float enabled",
                              InfoFrom(func), WebGLContext::EnumName(type));
         return validType;
     }
 
     /* OES_texture_half_float add types */
-    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+    if (type == LOCAL_GL_HALF_FLOAT) {
         bool validType = IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float);
         if (!validType)
             ErrorInvalidEnum("%s: invalid type %s: need OES_texture_half_float enabled",
                              InfoFrom(func), WebGLContext::EnumName(type));
         return validType;
     }
 
     /* WEBGL_depth_texture added types */
@@ -1021,105 +1021,16 @@ WebGLContext::ValidateTexSubImageSize(GL
         ErrorInvalidValue("%s: subtexture rectangle out-of-bounds", InfoFrom(func));
         return false;
     }
 
     return true;
 }
 
 /**
- * Return the bits per texel for format & type combination.
- * Assumes that format & type are a valid combination as checked with
- * ValidateTexImageFormatAndType().
- */
-uint32_t
-WebGLContext::GetBitsPerTexel(TexInternalFormat format, TexType type)
-{
-    /* Known fixed-sized types */
-    if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
-        type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
-        type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
-    {
-        return 16;
-    }
-
-    if (type == LOCAL_GL_UNSIGNED_INT_24_8)
-        return 32;
-
-    int bitsPerComponent = 0;
-    switch (type.get()) {
-    case LOCAL_GL_UNSIGNED_BYTE:
-        bitsPerComponent = 8;
-        break;
-
-    case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
-    case LOCAL_GL_UNSIGNED_SHORT:
-        bitsPerComponent = 16;
-        break;
-
-    case LOCAL_GL_FLOAT:
-    case LOCAL_GL_UNSIGNED_INT:
-        bitsPerComponent = 32;
-        break;
-
-    default:
-        MOZ_ASSERT(false, "Unhandled type.");
-        break;
-    }
-
-    switch (format.get()) {
-        // Uncompressed formats
-    case LOCAL_GL_ALPHA:
-    case LOCAL_GL_LUMINANCE:
-    case LOCAL_GL_DEPTH_COMPONENT:
-    case LOCAL_GL_DEPTH_STENCIL:
-        return 1 * bitsPerComponent;
-
-    case LOCAL_GL_LUMINANCE_ALPHA:
-        return 2 * bitsPerComponent;
-
-    case LOCAL_GL_RGB:
-    case LOCAL_GL_RGB32F:
-    case LOCAL_GL_SRGB_EXT:
-        return 3 * bitsPerComponent;
-
-    case LOCAL_GL_RGBA:
-    case LOCAL_GL_RGBA32F:
-    case LOCAL_GL_SRGB_ALPHA_EXT:
-        return 4 * bitsPerComponent;
-
-        // Compressed formats
-    case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
-    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
-        return 2;
-
-    case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-    case LOCAL_GL_ATC_RGB:
-    case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
-    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
-    case LOCAL_GL_ETC1_RGB8_OES:
-        return 4;
-
-    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
-    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
-    case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
-    case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
-        return 8;
-
-    default:
-        break;
-    }
-
-    MOZ_ASSERT(false, "Unhandled format+type combo.");
-    return 0;
-}
-
-/**
  * Perform validation of format/type combinations for TexImage variants.
  * Returns true if the format/type is a valid combination, false otherwise.
  */
 bool
 WebGLContext::ValidateTexImageFormatAndType(GLenum format,
                                             GLenum type, WebGLTexImageFunc func)
 {
     if (IsCompressedFunc(func) || IsCopyFunc(func))
@@ -1136,36 +1047,33 @@ WebGLContext::ValidateTexImageFormatAndT
     bool validCombo = false;
 
     switch (format) {
     case LOCAL_GL_ALPHA:
     case LOCAL_GL_LUMINANCE:
     case LOCAL_GL_LUMINANCE_ALPHA:
         validCombo = (type == LOCAL_GL_UNSIGNED_BYTE ||
                       type == LOCAL_GL_HALF_FLOAT ||
-                      type == LOCAL_GL_HALF_FLOAT_OES ||
                       type == LOCAL_GL_FLOAT);
         break;
 
     case LOCAL_GL_RGB:
     case LOCAL_GL_SRGB:
         validCombo = (type == LOCAL_GL_UNSIGNED_BYTE ||
                       type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 ||
                       type == LOCAL_GL_HALF_FLOAT ||
-                      type == LOCAL_GL_HALF_FLOAT_OES ||
                       type == LOCAL_GL_FLOAT);
         break;
 
     case LOCAL_GL_RGBA:
     case LOCAL_GL_SRGB_ALPHA:
         validCombo = (type == LOCAL_GL_UNSIGNED_BYTE ||
                       type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
                       type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
                       type == LOCAL_GL_HALF_FLOAT ||
-                      type == LOCAL_GL_HALF_FLOAT_OES ||
                       type == LOCAL_GL_FLOAT);
         break;
 
     case LOCAL_GL_DEPTH_COMPONENT:
         validCombo = (type == LOCAL_GL_UNSIGNED_SHORT ||
                       type == LOCAL_GL_UNSIGNED_INT);
         break;
 
@@ -1286,17 +1194,16 @@ WebGLContext::ValidateTexInputData(GLenu
 
     // First, we check for packed types
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
         validInput = (jsArrayType == -1 || jsArrayType == js::Scalar::Uint8);
         break;
 
     case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
     case LOCAL_GL_UNSIGNED_SHORT:
     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
         validInput = (jsArrayType == -1 || jsArrayType == js::Scalar::Uint16);
         break;
 
     case LOCAL_GL_UNSIGNED_INT:
@@ -1444,16 +1351,17 @@ WebGLContext::ValidateTexImage(GLuint di
     if (IsSubFunc(func)) {
         if (!tex->HasImageInfoAt(texImageTarget, level)) {
             ErrorInvalidOperation("%s: no texture image previously defined for target %s at level %d",
                                   info, WebGLContext::EnumName(texImageTarget.get()), level);
             return false;
         }
 
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
+
         if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset,
                                      width, height, depth,
                                      imageInfo.Width(), imageInfo.Height(), 0,
                                      func))
         {
             return false;
         }
     }
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -74,73 +74,66 @@ WebGLFramebuffer::Attachment::IsDefined(
 }
 
 bool
 WebGLFramebuffer::Attachment::HasAlpha() const
 {
     MOZ_ASSERT(HasImage());
 
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
-        return FormatHasAlpha(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat());
+        return FormatHasAlpha(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).EffectiveInternalFormat());
     else if (Renderbuffer())
         return FormatHasAlpha(Renderbuffer()->InternalFormat());
     else return false;
 }
 
 GLenum
 WebGLFramebuffer::GetFormatForAttachment(const WebGLFramebuffer::Attachment& attachment) const
 {
     MOZ_ASSERT(attachment.IsDefined());
     MOZ_ASSERT(attachment.Texture() || attachment.Renderbuffer());
 
     if (attachment.Texture()) {
         const WebGLTexture& tex = *attachment.Texture();
         MOZ_ASSERT(tex.HasImageInfoAt(attachment.ImageTarget(), 0));
 
         const WebGLTexture::ImageInfo& imgInfo = tex.ImageInfoAt(attachment.ImageTarget(), 0);
-        return imgInfo.InternalFormat().get();
+        return imgInfo.EffectiveInternalFormat().get();
     }
 
     if (attachment.Renderbuffer())
         return attachment.Renderbuffer()->InternalFormat();
 
     return LOCAL_GL_NONE;
 }
 
-bool
-WebGLFramebuffer::Attachment::IsReadableFloat() const
+TexInternalFormat
+WebGLFramebuffer::Attachment::EffectiveInternalFormat() const
 {
     const WebGLTexture* tex = Texture();
     if (tex && tex->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
-        GLenum type = tex->ImageInfoAt(mTexImageTarget, mTexImageLevel).Type().get();
-        switch (type) {
-        case LOCAL_GL_FLOAT:
-        case LOCAL_GL_HALF_FLOAT_OES:
-            return true;
-        }
-        return false;
+        return tex->ImageInfoAt(mTexImageTarget, mTexImageLevel).EffectiveInternalFormat();
     }
 
     const WebGLRenderbuffer* rb = Renderbuffer();
     if (rb) {
-        GLenum format = rb->InternalFormat();
-        switch (format) {
-        case LOCAL_GL_RGB16F:
-        case LOCAL_GL_RGBA16F:
-        case LOCAL_GL_RGB32F:
-        case LOCAL_GL_RGBA32F:
-            return true;
-        }
-        return false;
+        return rb->InternalFormat();
     }
 
-    // If we arrive here Attachment isn't correct setup because it has
-    // no texture nor render buffer pointer.
-    MOZ_ASSERT(false, "Should not get here.");
-    return false;
+    return LOCAL_GL_NONE;
+}
+
+bool
+WebGLFramebuffer::Attachment::IsReadableFloat() const
+{
+    TexInternalFormat internalformat = EffectiveInternalFormat();
+    MOZ_ASSERT(internalformat != LOCAL_GL_NONE);
+    TexType type = TypeFromInternalFormat(internalformat);
+    return type == LOCAL_GL_FLOAT ||
+           type == LOCAL_GL_HALF_FLOAT;
 }
 
 void
 WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level)
 {
     mTexturePtr = tex;
     mRenderbufferPtr = nullptr;
     mTexImageTarget = target;
@@ -225,65 +218,45 @@ WebGLFramebuffer::Attachment::RectangleO
     MOZ_CRASH("Should not get here.");
 }
 
 /* The following IsValidFBOTextureXXX functions check the internal
    format that is used by GL or GL ES texture formats.  This
    corresponds to the state that is stored in
    WebGLTexture::ImageInfo::InternalFormat()*/
 static inline bool
-IsValidFBOTextureColorFormat(GLenum internalFormat)
+IsValidFBOTextureColorFormat(TexInternalFormat internalformat)
 {
     /* These formats are internal formats for each texture -- the actual
      * low level format, which we might have to do conversions for when
      * running against desktop GL (e.g. GL_RGBA + GL_FLOAT -> GL_RGBA32F).
      *
      * This function just handles all of them whether desktop GL or ES.
      */
 
-    return (
-        /* linear 8-bit formats */
-        internalFormat == LOCAL_GL_ALPHA ||
-        internalFormat == LOCAL_GL_LUMINANCE ||
-        internalFormat == LOCAL_GL_LUMINANCE_ALPHA ||
-        internalFormat == LOCAL_GL_RGB ||
-        internalFormat == LOCAL_GL_RGBA ||
-        /* sRGB 8-bit formats */
-        internalFormat == LOCAL_GL_SRGB_EXT ||
-        internalFormat == LOCAL_GL_SRGB_ALPHA_EXT ||
-        /* linear float32 formats */
-        internalFormat == LOCAL_GL_ALPHA32F_ARB ||
-        internalFormat == LOCAL_GL_LUMINANCE32F_ARB ||
-        internalFormat == LOCAL_GL_LUMINANCE_ALPHA32F_ARB ||
-        internalFormat == LOCAL_GL_RGB32F_ARB ||
-        internalFormat == LOCAL_GL_RGBA32F_ARB ||
-        /* texture_half_float formats */
-        internalFormat == LOCAL_GL_ALPHA16F_ARB ||
-        internalFormat == LOCAL_GL_LUMINANCE16F_ARB ||
-        internalFormat == LOCAL_GL_LUMINANCE_ALPHA16F_ARB ||
-        internalFormat == LOCAL_GL_RGB16F_ARB ||
-        internalFormat == LOCAL_GL_RGBA16F_ARB
-    );
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_ALPHA ||
+           unsizedformat == LOCAL_GL_LUMINANCE ||
+           unsizedformat == LOCAL_GL_LUMINANCE_ALPHA ||
+           unsizedformat == LOCAL_GL_RGB ||
+           unsizedformat == LOCAL_GL_RGBA ||
+           unsizedformat == LOCAL_GL_SRGB ||
+           unsizedformat == LOCAL_GL_SRGB_ALPHA;
 }
 
 static inline bool
-IsValidFBOTextureDepthFormat(GLenum internalFormat)
+IsValidFBOTextureDepthFormat(GLenum internalformat)
 {
-    return (
-        internalFormat == LOCAL_GL_DEPTH_COMPONENT ||
-        internalFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
-        internalFormat == LOCAL_GL_DEPTH_COMPONENT32);
+    return IsGLDepthFormat(internalformat);
 }
 
 static inline bool
-IsValidFBOTextureDepthStencilFormat(GLenum internalFormat)
+IsValidFBOTextureDepthStencilFormat(GLenum internalformat)
 {
-    return (
-        internalFormat == LOCAL_GL_DEPTH_STENCIL ||
-        internalFormat == LOCAL_GL_DEPTH24_STENCIL8);
+    return IsGLDepthStencilFormat(internalformat);
 }
 
 /* The following IsValidFBORenderbufferXXX functions check the internal
    format that is stored by WebGLRenderbuffer::InternalFormat(). Valid
    values can be found in WebGLContext::RenderbufferStorage. */
 static inline bool
 IsValidFBORenderbufferColorFormat(GLenum internalFormat)
 {
@@ -325,17 +298,17 @@ WebGLFramebuffer::Attachment::IsComplete
     {
         return false;
     }
 
     if (Texture()) {
         MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
         const WebGLTexture::ImageInfo& imageInfo =
             Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
-        GLenum internalformat = imageInfo.InternalFormat().get();
+        GLenum internalformat = imageInfo.EffectiveInternalFormat().get();
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
             return IsValidFBOTextureDepthFormat(internalformat);
 
         if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT)
             return false; // Textures can't have the correct format for stencil buffers
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -48,16 +48,18 @@ public:
 
         explicit Attachment(FBAttachment aAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0);
         ~Attachment();
 
         bool IsDefined() const;
 
         bool IsDeleteRequested() const;
 
+        TexInternalFormat EffectiveInternalFormat() const;
+
         bool HasAlpha() const;
         bool IsReadableFloat() const;
 
         void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level);
         void SetRenderbuffer(WebGLRenderbuffer* rb);
 
         const WebGLTexture* Texture() const {
             return mTexturePtr;
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLInternalFormatsTable.h
@@ -0,0 +1,82 @@
+// intentionally no include guard here.
+
+#ifndef HANDLE_WEBGL_INTERNAL_FORMAT
+#error This header is meant to be included by other files defining HANDLE_WEBGL_INTERNAL_FORMAT.
+#endif
+
+#define WEBGL_INTERNAL_FORMAT(effectiveinternalformat, unsizedinternalformat, type) \
+  HANDLE_WEBGL_INTERNAL_FORMAT(LOCAL_GL_##effectiveinternalformat, \
+                               LOCAL_GL_##unsizedinternalformat, \
+                               LOCAL_GL_##type)
+
+// OpenGL ES 3.0.3, Table 3.2
+//
+// Maps effective internal formats to (unsized internal format, type) pairs.
+//
+//                    Effective int. fmt.     Unsized int. fmt.   Type
+WEBGL_INTERNAL_FORMAT(ALPHA8,                 ALPHA,              UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(LUMINANCE8,             LUMINANCE,          UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(LUMINANCE8_ALPHA8,      LUMINANCE_ALPHA,    UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RGB8,                   RGB,                UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RGBA4,                  RGBA,               UNSIGNED_SHORT_4_4_4_4)
+WEBGL_INTERNAL_FORMAT(RGB5_A1,                RGBA,               UNSIGNED_SHORT_5_5_5_1)
+WEBGL_INTERNAL_FORMAT(RGBA8,                  RGBA,               UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RGB10_A2,               RGB,                UNSIGNED_INT_2_10_10_10_REV)
+WEBGL_INTERNAL_FORMAT(DEPTH_COMPONENT16,      DEPTH_COMPONENT,    UNSIGNED_SHORT)
+WEBGL_INTERNAL_FORMAT(DEPTH_COMPONENT24,      DEPTH_COMPONENT,    UNSIGNED_INT)
+WEBGL_INTERNAL_FORMAT(R8,                     RED,                UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RG8,                    RG,                 UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(R16F,                   RED,                HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(R32F,                   RED,                FLOAT)
+WEBGL_INTERNAL_FORMAT(RG16F,                  RG,                 HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(RG32F,                  RG,                 UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(R8I,                    RED_INTEGER,        BYTE)
+WEBGL_INTERNAL_FORMAT(R8UI,                   RED_INTEGER,        UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(R16I,                   RED_INTEGER,        BYTE)
+WEBGL_INTERNAL_FORMAT(R16UI,                  RED_INTEGER,        UNSIGNED_SHORT)
+WEBGL_INTERNAL_FORMAT(R32I,                   RED_INTEGER,        INT)
+WEBGL_INTERNAL_FORMAT(R32UI,                  RED_INTEGER,        UNSIGNED_INT)
+WEBGL_INTERNAL_FORMAT(RG8I,                   RG_INTEGER,         BYTE)
+WEBGL_INTERNAL_FORMAT(RG8UI,                  RG_INTEGER,         UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RG16I,                  RG_INTEGER,         SHORT)
+WEBGL_INTERNAL_FORMAT(RG16UI,                 RG_INTEGER,         UNSIGNED_SHORT)
+WEBGL_INTERNAL_FORMAT(RG32I,                  RG_INTEGER,         INT)
+WEBGL_INTERNAL_FORMAT(RG32UI,                 RG_INTEGER,         UNSIGNED_INT)
+WEBGL_INTERNAL_FORMAT(RGBA32F,                RGBA,               FLOAT)
+WEBGL_INTERNAL_FORMAT(RGB32F,                 RGB,                FLOAT)
+WEBGL_INTERNAL_FORMAT(ALPHA32F_EXT,           ALPHA,              FLOAT)
+WEBGL_INTERNAL_FORMAT(LUMINANCE32F_EXT,       LUMINANCE,          FLOAT)
+WEBGL_INTERNAL_FORMAT(LUMINANCE_ALPHA32F_EXT, LUMINANCE_ALPHA,    FLOAT)
+WEBGL_INTERNAL_FORMAT(RGBA16F,                RGBA,               HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(RGB16F,                 RGB,                HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(ALPHA16F_EXT,           ALPHA,              HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(LUMINANCE16F_EXT,       LUMINANCE,          HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(LUMINANCE_ALPHA16F_EXT, LUMINANCE_ALPHA,    HALF_FLOAT)
+WEBGL_INTERNAL_FORMAT(DEPTH24_STENCIL8,       DEPTH_STENCIL,      UNSIGNED_INT_24_8)
+WEBGL_INTERNAL_FORMAT(R11F_G11F_B10F,         RGB,                UNSIGNED_INT_10F_11F_11F_REV)
+WEBGL_INTERNAL_FORMAT(RGB9_E5,                RGB,                UNSIGNED_INT_5_9_9_9_REV)
+WEBGL_INTERNAL_FORMAT(SRGB8,                  SRGB,               UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(SRGB8_ALPHA8,           SRGB_ALPHA,         UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(DEPTH_COMPONENT32F,     DEPTH_COMPONENT,    FLOAT)
+WEBGL_INTERNAL_FORMAT(DEPTH32F_STENCIL8,      DEPTH_STENCIL,      FLOAT_32_UNSIGNED_INT_24_8_REV)
+WEBGL_INTERNAL_FORMAT(RGB565,                 RGB,                UNSIGNED_SHORT_5_6_5)
+WEBGL_INTERNAL_FORMAT(RGBA32UI,               RGBA_INTEGER,       UNSIGNED_INT)
+WEBGL_INTERNAL_FORMAT(RGB32UI,                RGB_INTEGER,        UNSIGNED_INT)
+WEBGL_INTERNAL_FORMAT(RGBA16UI,               RGBA_INTEGER,       UNSIGNED_SHORT)
+WEBGL_INTERNAL_FORMAT(RGB16UI,                RGB_INTEGER,        UNSIGNED_SHORT)
+WEBGL_INTERNAL_FORMAT(RGBA8UI,                RGBA_INTEGER,       UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RGB8UI,                 RGB_INTEGER,        UNSIGNED_BYTE)
+WEBGL_INTERNAL_FORMAT(RGBA32I,                RGBA_INTEGER,       INT)
+WEBGL_INTERNAL_FORMAT(RGB32I,                 RGB_INTEGER,        INT)
+WEBGL_INTERNAL_FORMAT(RGBA16I,                RGBA_INTEGER,       SHORT)
+WEBGL_INTERNAL_FORMAT(RGB16I,                 RGB_INTEGER,        SHORT)
+WEBGL_INTERNAL_FORMAT(RGBA8I,                 RGBA_INTEGER,       BYTE)
+WEBGL_INTERNAL_FORMAT(RGB8I,                  RGB_INTEGER,        BYTE)
+WEBGL_INTERNAL_FORMAT(R8_SNORM,               RED,                BYTE)
+WEBGL_INTERNAL_FORMAT(RG8_SNORM,              RG,                 BYTE)
+WEBGL_INTERNAL_FORMAT(RGB8_SNORM,             RGB,                BYTE)
+WEBGL_INTERNAL_FORMAT(RGBA8_SNORM,            RGBA,               BYTE)
+WEBGL_INTERNAL_FORMAT(RGB10_A2UI,             RGBA_INTEGER,       UNSIGNED_INT_2_10_10_10_REV)
+
+#undef WEBGL_INTERNAL_FORMAT
+#undef HANDLE_WEBGL_INTERNAL_FORMAT
--- a/dom/canvas/WebGLStrongTypes.h
+++ b/dom/canvas/WebGLStrongTypes.h
@@ -294,16 +294,19 @@ STRONG_GLENUM_END(TexFormat)
 STRONG_GLENUM_BEGIN(TexInternalFormat)
     STRONG_GLENUM_VALUE(NONE),
     STRONG_GLENUM_VALUE(DEPTH_COMPONENT),
     STRONG_GLENUM_VALUE(ALPHA),
     STRONG_GLENUM_VALUE(RGB),
     STRONG_GLENUM_VALUE(RGBA),
     STRONG_GLENUM_VALUE(LUMINANCE),
     STRONG_GLENUM_VALUE(LUMINANCE_ALPHA),
+    STRONG_GLENUM_VALUE(ALPHA8),
+    STRONG_GLENUM_VALUE(LUMINANCE8),
+    STRONG_GLENUM_VALUE(LUMINANCE8_ALPHA8),
     STRONG_GLENUM_VALUE(RGB8),
     STRONG_GLENUM_VALUE(RGBA4),
     STRONG_GLENUM_VALUE(RGB5_A1),
     STRONG_GLENUM_VALUE(RGBA8),
     STRONG_GLENUM_VALUE(RGB10_A2),
     STRONG_GLENUM_VALUE(DEPTH_COMPONENT16),
     STRONG_GLENUM_VALUE(DEPTH_COMPONENT24),
     STRONG_GLENUM_VALUE(R8),
@@ -327,18 +330,24 @@ STRONG_GLENUM_BEGIN(TexInternalFormat)
     STRONG_GLENUM_VALUE(COMPRESSED_RGB_S3TC_DXT1_EXT),
     STRONG_GLENUM_VALUE(COMPRESSED_RGBA_S3TC_DXT1_EXT),
     STRONG_GLENUM_VALUE(COMPRESSED_RGBA_S3TC_DXT3_EXT),
     STRONG_GLENUM_VALUE(COMPRESSED_RGBA_S3TC_DXT5_EXT),
     STRONG_GLENUM_VALUE(DEPTH_STENCIL),
     STRONG_GLENUM_VALUE(ATC_RGBA_INTERPOLATED_ALPHA),
     STRONG_GLENUM_VALUE(RGBA32F),
     STRONG_GLENUM_VALUE(RGB32F),
+    STRONG_GLENUM_VALUE(ALPHA32F_EXT),
+    STRONG_GLENUM_VALUE(LUMINANCE32F_EXT),
+    STRONG_GLENUM_VALUE(LUMINANCE_ALPHA32F_EXT),
     STRONG_GLENUM_VALUE(RGBA16F),
     STRONG_GLENUM_VALUE(RGB16F),
+    STRONG_GLENUM_VALUE(ALPHA16F_EXT),
+    STRONG_GLENUM_VALUE(LUMINANCE16F_EXT),
+    STRONG_GLENUM_VALUE(LUMINANCE_ALPHA16F_EXT),
     STRONG_GLENUM_VALUE(DEPTH24_STENCIL8),
     STRONG_GLENUM_VALUE(COMPRESSED_RGB_PVRTC_4BPPV1),
     STRONG_GLENUM_VALUE(COMPRESSED_RGB_PVRTC_2BPPV1),
     STRONG_GLENUM_VALUE(COMPRESSED_RGBA_PVRTC_4BPPV1),
     STRONG_GLENUM_VALUE(COMPRESSED_RGBA_PVRTC_2BPPV1),
     STRONG_GLENUM_VALUE(R11F_G11F_B10F),
     STRONG_GLENUM_VALUE(RGB9_E5),
     STRONG_GLENUM_VALUE(SRGB),
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -47,17 +47,17 @@ WebGLTexture::Delete() {
     mContext->gl->fDeleteTextures(1, &mGLName);
     LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
 }
 
 int64_t
 WebGLTexture::ImageInfo::MemoryUsage() const {
     if (mImageDataStatus == WebGLImageDataStatus::NoImageData)
         return 0;
-    int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mInternalFormat, mType);
+    int64_t bitsPerTexel = GetBitsPerTexel(mEffectiveInternalFormat);
     return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8;
 }
 
 int64_t
 WebGLTexture::MemoryUsage() const {
     if (IsDeleted())
         return 0;
     int64_t result = 0;
@@ -133,26 +133,25 @@ WebGLTexture::Bind(TexTarget aTexTarget)
         if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES())
             mContext->gl->fTexParameteri(aTexTarget.get(), LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
     }
 }
 
 void
 WebGLTexture::SetImageInfo(TexImageTarget aTexImageTarget, GLint aLevel,
                            GLsizei aWidth, GLsizei aHeight,
-                           TexInternalFormat aInternalFormat, TexType aType,
-                           WebGLImageDataStatus aStatus)
+                           TexInternalFormat aEffectiveInternalFormat, WebGLImageDataStatus aStatus)
 {
     MOZ_ASSERT(TexImageTargetToTexTarget(aTexImageTarget) == mTarget);
     if (TexImageTargetToTexTarget(aTexImageTarget) != mTarget)
         return;
 
     EnsureMaxLevelWithCustomImagesAtLeast(aLevel);
 
-    ImageInfoAt(aTexImageTarget, aLevel) = ImageInfo(aWidth, aHeight, aInternalFormat, aType, aStatus);
+    ImageInfoAt(aTexImageTarget, aLevel) = ImageInfo(aWidth, aHeight, aEffectiveInternalFormat, aStatus);
 
     if (aLevel > 0)
         SetCustomMipmap();
 
     // Invalidate framebuffer status cache
     NotifyFBsStatusChanged();
 
     SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
@@ -324,17 +323,19 @@ WebGLTexture::ResolvedFakeBlackStatus() 
                 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
                             "with some level 0 image having width or height not a power of two, and with a wrap mode "
                             "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
                 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
             }
         }
     }
 
-    if (ImageInfoBase().mType == LOCAL_GL_FLOAT &&
+    TexType type = TypeFromInternalFormat(ImageInfoBase().mEffectiveInternalFormat);
+
+    if (type == LOCAL_GL_FLOAT &&
         !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear))
     {
         if (mMinFilter == LOCAL_GL_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
             mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear minification filter, "
@@ -344,17 +345,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
         }
         else if (mMagFilter == LOCAL_GL_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
                                       "which is not compatible with gl.FLOAT by default. "
                                       "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
         }
-    } else if (ImageInfoBase().mType == LOCAL_GL_HALF_FLOAT_OES &&
+    } else if (type == LOCAL_GL_HALF_FLOAT &&
                !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear))
     {
         if (mMinFilter == LOCAL_GL_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
             mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear minification filter, "
@@ -529,47 +530,49 @@ void
 WebGLTexture::DoDeferredImageInitialization(TexImageTarget imageTarget, GLint level)
 {
     const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
     MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
 
     mContext->MakeContextCurrent();
 
     // Try to clear with glCLear.
-    TexInternalFormat internalformat = imageInfo.mInternalFormat;
-    TexType type = imageInfo.mType;
-    WebGLTexelFormat texelformat = GetWebGLTexelFormat(internalformat, type);
 
     bool cleared = ClearWithTempFB(mContext, GLName(),
                                    imageTarget, level,
-                                   internalformat, imageInfo.mHeight, imageInfo.mWidth);
+                                   imageInfo.mEffectiveInternalFormat,
+                                   imageInfo.mHeight, imageInfo.mWidth);
     if (cleared) {
         SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
         return;
     }
 
     // That didn't work. Try uploading zeros then.
     gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget.get());
 
-    uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat);
+    size_t bitspertexel = GetBitsPerTexel(imageInfo.mEffectiveInternalFormat);
+    MOZ_ASSERT((bitspertexel % 8) == 0); // that would only happen for compressed images, which
+                                         // cannot use deferred initialization.
+    size_t bytespertexel = bitspertexel / 8;
     CheckedUint32 checked_byteLength
         = WebGLContext::GetImageSize(
                         imageInfo.mHeight,
                         imageInfo.mWidth,
-                        texelsize,
+                        bytespertexel,
                         mContext->mPixelStoreUnpackAlignment);
     MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
     ScopedFreePtr<void> zeros;
     zeros = calloc(1, checked_byteLength.value());
 
     gl::GLContext* gl = mContext->gl;
-    GLenum driverType = DriverTypeFromType(gl, type);
     GLenum driverInternalFormat = LOCAL_GL_NONE;
     GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromFormatAndType(gl, internalformat, type, &driverInternalFormat, &driverFormat);
+    GLenum driverType = LOCAL_GL_NONE;
+    DriverFormatsFromEffectiveInternalFormat(gl, imageInfo.mEffectiveInternalFormat,
+                                             &driverInternalFormat, &driverFormat, &driverType);
 
     mContext->GetAndFlushUnderlyingGLErrors();
     gl->fTexImage2D(imageTarget.get(), level, driverInternalFormat,
                     imageInfo.mWidth, imageInfo.mHeight,
                     0, driverFormat, driverType,
                     zeros);
     GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
     if (error) {
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -62,41 +62,37 @@ protected:
 
 public:
 
     class ImageInfo
         : public WebGLRectangleObject
     {
     public:
         ImageInfo()
-            : mInternalFormat(LOCAL_GL_NONE)
-            , mType(LOCAL_GL_NONE)
+            : mEffectiveInternalFormat(LOCAL_GL_NONE)
             , mImageDataStatus(WebGLImageDataStatus::NoImageData)
         {}
 
         ImageInfo(GLsizei width,
                   GLsizei height,
-                  TexInternalFormat internalFormat,
-                  TexType type,
+                  TexInternalFormat effectiveInternalFormat,
                   WebGLImageDataStatus status)
             : WebGLRectangleObject(width, height)
-            , mInternalFormat(internalFormat)
-            , mType(type)
+            , mEffectiveInternalFormat(effectiveInternalFormat)
             , mImageDataStatus(status)
         {
             // shouldn't use this constructor to construct a null ImageInfo
             MOZ_ASSERT(status != WebGLImageDataStatus::NoImageData);
         }
 
         bool operator==(const ImageInfo& a) const {
             return mImageDataStatus == a.mImageDataStatus &&
                    mWidth == a.mWidth &&
                    mHeight == a.mHeight &&
-                   mInternalFormat == a.mInternalFormat &&
-                   mType == a.mType;
+                   mEffectiveInternalFormat == a.mEffectiveInternalFormat;
         }
         bool operator!=(const ImageInfo& a) const {
             return !(*this == a);
         }
         bool IsSquare() const {
             return mWidth == mHeight;
         }
         bool IsPositive() const {
@@ -105,31 +101,27 @@ public:
         bool IsPowerOfTwo() const {
             return is_pot_assuming_nonnegative(mWidth) &&
                    is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...)
         }
         bool HasUninitializedImageData() const {
             return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData;
         }
         int64_t MemoryUsage() const;
-        /*! This is the format passed from JS to WebGL.
-         * It can be converted to a value to be passed to driver with
-         * DriverFormatsFromFormatAndType().
-         */
-        TexInternalFormat InternalFormat() const { return mInternalFormat; }
 
-        /*! This is the type passed from JS to WebGL.
-         * It can be converted to a value to be passed to driver with
-         * DriverTypeFromType().
-         */
-        TexType Type() const { return mType; }
+        TexInternalFormat EffectiveInternalFormat() const { return mEffectiveInternalFormat; }
 
     protected:
-        TexInternalFormat mInternalFormat; //!< This is the WebGL/GLES internal format.
-        TexType mType;   //!< This is the WebGL/GLES type
+        /*
+         * This is the "effective internal format" of the texture,
+         * an official OpenGL spec concept, see
+         * OpenGL ES 3.0.3 spec, section 3.8.3, page 126 and below.
+         */
+        TexInternalFormat mEffectiveInternalFormat;
+
         WebGLImageDataStatus mImageDataStatus;
 
         friend class WebGLTexture;
     };
 
 private:
     static size_t FaceForTarget(TexImageTarget texImageTarget) {
         if (texImageTarget == LOCAL_GL_TEXTURE_2D)
@@ -225,18 +217,17 @@ protected:
     bool DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const;
 
 public:
 
     void Bind(TexTarget aTexTarget);
 
     void SetImageInfo(TexImageTarget aTarget, GLint aLevel,
                       GLsizei aWidth, GLsizei aHeight,
-                      TexInternalFormat aInternalFormat, TexType aType,
-                      WebGLImageDataStatus aStatus);
+                      TexInternalFormat aFormat, WebGLImageDataStatus aStatus);
 
     void SetMinFilter(TexMinFilter aMinFilter) {
         mMinFilter = aMinFilter;
         SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
     }
     void SetMagFilter(TexMagFilter aMagFilter) {
         mMagFilter = aMagFilter;
         SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -2360,17 +2360,16 @@ bool
 GLContext::ResizeScreenBuffer(const IntSize& size)
 {
     if (!IsOffscreenSizeAllowed(size))
         return false;
 
     return mScreen->Resize(size);
 }
 
-
 void
 GLContext::DestroyScreenBuffer()
 {
     mScreen = nullptr;
 }
 
 void
 GLContext::ForceDirtyScreen()