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 209245 97068dca449ef5b8afa16bc54975d6aab02ec168
parent 209244 e91da451577285cc36cf6cc2082a6563d6f7ac09
child 209246 c78b949b21da028939c2dac178e06fce5984cb8c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjgilbert
bugs1077183
milestone35.0a1
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()