servo: Merge #10443 - webgl: Implement the pending texImage2D overload, and add more validation (from emilio:webgl-teximage2d-overload); r=jdm
authorEmilio Cobos Álvarez <me@emiliocobos.me>
Fri, 22 Apr 2016 11:25:29 -0700
changeset 338593 210ee0ba28fe875b2e7d4e1df27dbaf89f5bf83b
parent 338592 ca063f0e8489f6b4e17f9feceb163130b23f9cc6
child 338594 8a207117a1d4bb3777d352ebb1d4e6cf0f681f0b
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #10443 - webgl: Implement the pending texImage2D overload, and add more validation (from emilio:webgl-teximage2d-overload); r=jdm This is a large-ish refactor of the Texture2D code, but it should be easier to read and of course more correct. I tried to annotate every error condition with a spec paragraph. I made just a reftest to ensure this works as intended, since I expect #10373 to land pretty soon. Source-Repo: https://github.com/servo/servo Source-Revision: f1defb446e8cd4e36231acae77f11c72c74964b0
servo/components/script/dom/webglprogram.rs
servo/components/script/dom/webglrenderingcontext.rs
servo/components/script/dom/webglshader.rs
servo/components/script/dom/webgltexture.rs
servo/components/script/dom/webgluniformlocation.rs
servo/components/script/dom/webidls/WebGLRenderingContext.webidl
--- a/servo/components/script/dom/webglprogram.rs
+++ b/servo/components/script/dom/webglprogram.rs
@@ -18,28 +18,30 @@ use std::cell::Cell;
 use util::str::DOMString;
 use webrender_traits::{WebGLCommand, WebGLError, WebGLParameter, WebGLResult};
 
 #[dom_struct]
 pub struct WebGLProgram {
     webgl_object: WebGLObject,
     id: u32,
     is_deleted: Cell<bool>,
+    linked: Cell<bool>,
     fragment_shader: MutNullableHeap<JS<WebGLShader>>,
     vertex_shader: MutNullableHeap<JS<WebGLShader>>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
     renderer: IpcSender<CanvasMsg>,
 }
 
 impl WebGLProgram {
     fn new_inherited(renderer: IpcSender<CanvasMsg>, id: u32) -> WebGLProgram {
         WebGLProgram {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             is_deleted: Cell::new(false),
+            linked: Cell::new(false),
             fragment_shader: Default::default(),
             vertex_shader: Default::default(),
             renderer: renderer,
         }
     }
 
     pub fn maybe_new(global: GlobalRef, renderer: IpcSender<CanvasMsg>)
                      -> Option<Root<WebGLProgram>> {
@@ -66,29 +68,37 @@ impl WebGLProgram {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
             let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteProgram(self.id)));
         }
     }
 
     /// glLinkProgram
     pub fn link(&self) {
+        self.linked.set(false);
+
+        match self.fragment_shader.get() {
+            Some(ref shader) if shader.successfully_compiled() => {},
+            _ => return,
+        }
+
+        match self.vertex_shader.get() {
+            Some(ref shader) if shader.successfully_compiled() => {},
+            _ => return,
+        }
+
+        self.linked.set(true);
+
         self.renderer.send(CanvasMsg::WebGL(WebGLCommand::LinkProgram(self.id))).unwrap();
     }
 
     /// glUseProgram
     pub fn use_program(&self) -> WebGLResult<()> {
-        match self.fragment_shader.get() {
-            Some(ref shader) if shader.successfully_compiled() => {},
-            _ => return Err(WebGLError::InvalidOperation),
-        }
-
-        match self.vertex_shader.get() {
-            Some(ref shader) if shader.successfully_compiled() => {},
-            _ => return Err(WebGLError::InvalidOperation),
+        if !self.linked.get() {
+            return Err(WebGLError::InvalidOperation);
         }
 
         self.renderer.send(CanvasMsg::WebGL(WebGLCommand::UseProgram(self.id))).unwrap();
         Ok(())
     }
 
     /// glAttachShader
     pub fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
--- a/servo/components/script/dom/webglrenderingcontext.rs
+++ b/servo/components/script/dom/webglrenderingcontext.rs
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use canvas_traits::{CanvasCommonMsg, CanvasMsg};
-use dom::bindings::codegen::Bindings::WebGLActiveInfoBinding::WebGLActiveInfoMethods;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{WebGLRenderingContextMethods};
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
 use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
-use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_to_vec_checked, array_buffer_view_to_vec};
+use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_data_checked};
+use dom::bindings::conversions::{array_buffer_view_to_vec_checked, array_buffer_view_to_vec};
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
 use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::htmlcanvaselement::HTMLCanvasElement;
 use dom::htmlcanvaselement::utils as canvas_utils;
 use dom::node::{Node, NodeDamage, window_from_node};
@@ -62,55 +62,16 @@ bitflags! {
     #[derive(HeapSizeOf, JSTraceable)]
     flags TextureUnpacking: u8 {
         const FLIP_Y_AXIS = 0x01,
         const PREMULTIPLY_ALPHA = 0x02,
         const CONVERT_COLORSPACE = 0x04,
     }
 }
 
-pub enum UniformType {
-    Int,
-    IntVec2,
-    IntVec3,
-    IntVec4,
-    Float,
-    FloatVec2,
-    FloatVec3,
-    FloatVec4,
-}
-
-impl UniformType {
-    fn element_count(&self) -> usize {
-        match *self {
-            UniformType::Int => 1,
-            UniformType::IntVec2 => 2,
-            UniformType::IntVec3 => 3,
-            UniformType::IntVec4 => 4,
-            UniformType::Float => 1,
-            UniformType::FloatVec2 => 2,
-            UniformType::FloatVec3 => 3,
-            UniformType::FloatVec4 => 4,
-        }
-    }
-
-    fn as_gl_constant(&self) -> u32 {
-        match *self {
-            UniformType::Int => constants::INT,
-            UniformType::IntVec2 => constants::INT_VEC2,
-            UniformType::IntVec3 => constants::INT_VEC3,
-            UniformType::IntVec4 => constants::INT_VEC4,
-            UniformType::Float => constants::FLOAT,
-            UniformType::FloatVec2 => constants::FLOAT_VEC2,
-            UniformType::FloatVec3 => constants::FLOAT_VEC3,
-            UniformType::FloatVec4 => constants::FLOAT_VEC4,
-        }
-    }
-}
-
 #[dom_struct]
 pub struct WebGLRenderingContext {
     reflector_: Reflector,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
     ipc_renderer: IpcSender<CanvasMsg>,
     #[ignore_heap_size_of = "Defined in offscreen_gl_context"]
     limits: GLLimits,
     canvas: JS<HTMLCanvasElement>,
@@ -219,62 +180,234 @@ impl WebGLRenderingContext {
             .unwrap();
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml
     // https://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf#nameddest=section-2.10.4
     fn validate_uniform_parameters<T>(&self,
                                    uniform: Option<&WebGLUniformLocation>,
-                                   type_: UniformType,
+                                   uniform_type: UniformSetterType,
                                    data: Option<&[T]>) -> bool {
         let uniform = match uniform {
             Some(uniform) => uniform,
             None => return false,
         };
 
         let program = self.current_program.get();
-        let program = match program {
-            Some(ref program) if program.id() == uniform.program_id() => program,
+        match program {
+            Some(ref program) if program.id() == uniform.program_id() => {},
             _ => {
                 self.webgl_error(InvalidOperation);
                 return false;
             },
         };
 
         let data = match data {
             Some(data) => data,
             None => {
                 self.webgl_error(InvalidOperation);
                 return false;
             },
         };
 
-        // TODO(autrilla): Don't request this every time, cache it
-        let active_uniform = match program.get_active_uniform(
-            uniform.id() as u32) {
-            Ok(active_uniform) => active_uniform,
-            Err(_) => {
-                self.webgl_error(InvalidOperation);
+        // TODO(emilio): Get more complex uniform info from ANGLE, and use it to
+        // properly validate that the uniform setter type is compatible with the
+        // uniform type, and that the uniform size matches.
+        if data.len() % uniform_type.element_count() != 0 {
+            self.webgl_error(InvalidOperation);
+            return false;
+        }
+
+        true
+    }
+
+    fn validate_tex_image_parameters(&self,
+                                     target: u32,
+                                     level: i32,
+                                     internal_format: u32,
+                                     width: i32,
+                                     height: i32,
+                                     border: i32,
+                                     format: u32,
+                                     data_type: u32) -> bool {
+        // GL_INVALID_ENUM is generated if target is not GL_TEXTURE_2D,
+        // GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+        // GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+        // GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
+        let texture = match target {
+            constants::TEXTURE_2D
+                => self.bound_texture_2d.get(),
+            constants::TEXTURE_CUBE_MAP_POSITIVE_X |
+            constants::TEXTURE_CUBE_MAP_NEGATIVE_X |
+            constants::TEXTURE_CUBE_MAP_POSITIVE_Y |
+            constants::TEXTURE_CUBE_MAP_NEGATIVE_Y |
+            constants::TEXTURE_CUBE_MAP_POSITIVE_Z |
+            constants::TEXTURE_CUBE_MAP_NEGATIVE_Z
+                => self.bound_texture_cube_map.get(),
+            _ => {
+                self.webgl_error(InvalidEnum);
                 return false;
             },
         };
 
-        if data.len() % type_.element_count() != 0 ||
-            (data.len() / type_.element_count() > active_uniform.Size() as usize) {
-                self.webgl_error(InvalidOperation);
+        //  If an attempt is made to call this function with no
+        //  WebGLTexture bound, an INVALID_OPERATION error is generated.
+        if texture.is_none() {
+            self.webgl_error(InvalidOperation);
+            return false;
+        }
+
+        // GL_INVALID_ENUM is generated if data_type is not an accepted value.
+        match data_type {
+            constants::UNSIGNED_BYTE |
+            constants::UNSIGNED_SHORT_4_4_4_4 |
+            constants::UNSIGNED_SHORT_5_5_5_1 |
+            constants::UNSIGNED_SHORT_5_6_5 => {},
+            _ => {
+                self.webgl_error(InvalidEnum);
                 return false;
+            },
         }
 
-        if type_.as_gl_constant() != active_uniform.Type() {
+
+        // TODO(emilio): GL_INVALID_VALUE may be generated if
+        // level is greater than log_2(max), where max is
+        // the returned value of GL_MAX_TEXTURE_SIZE when
+        // target is GL_TEXTURE_2D or GL_MAX_CUBE_MAP_TEXTURE_SIZE
+        // when target is not GL_TEXTURE_2D.
+        let is_cubic = target != constants::TEXTURE_2D;
+
+        // GL_INVALID_VALUE is generated if target is one of the
+        // six cube map 2D image targets and the width and height
+        // parameters are not equal.
+        if is_cubic && width != height {
+            self.webgl_error(InvalidValue);
+            return false;
+        }
+
+        // GL_INVALID_VALUE is generated if internal_format is not an
+        // accepted format.
+        match internal_format {
+            constants::DEPTH_COMPONENT |
+            constants::ALPHA |
+            constants::RGB |
+            constants::RGBA |
+            constants::LUMINANCE |
+            constants::LUMINANCE_ALPHA => {},
+
+            _ => {
+                self.webgl_error(InvalidValue);
+                return false;
+            },
+        }
+
+        // GL_INVALID_OPERATION is generated if format does not
+        // match internal_format.
+        if format != internal_format {
             self.webgl_error(InvalidOperation);
             return false;
         }
 
-        return true;
+        // GL_INVALID_VALUE is generated if level is less than 0.
+        //
+        // GL_INVALID_VALUE is generated if width or height is less than 0
+        // or greater than GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or
+        // GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D.
+        //
+        // TODO(emilio): Check limits
+        if width < 0 || height < 0 || level < 0 {
+            self.webgl_error(InvalidValue);
+            return false;
+        }
+
+        // GL_INVALID_VALUE is generated if level is greater than zero and the
+        // texture is not power of two.
+        if level > 0 &&
+           (!(width as u32).is_power_of_two() ||
+            !(height as u32).is_power_of_two()) {
+            self.webgl_error(InvalidValue);
+            return false;
+        }
+
+        // GL_INVALID_VALUE is generated if border is not 0.
+        if border != 0 {
+            self.webgl_error(InvalidValue);
+            return false;
+        }
+
+        // GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_4_4_4_4 or
+        // GL_UNSIGNED_SHORT_5_5_5_1 and format is not GL_RGBA.
+        //
+        // GL_INVALID_OPERATION is generated if type is
+        // GL_UNSIGNED_SHORT_5_6_5 and format is not GL_RGB.
+        match data_type {
+            constants::UNSIGNED_SHORT_4_4_4_4 |
+            constants::UNSIGNED_SHORT_5_5_5_1 if format != constants::RGBA => {
+                self.webgl_error(InvalidOperation);
+                return false;
+            },
+            constants::UNSIGNED_SHORT_5_6_5 if format != constants::RGB => {
+                self.webgl_error(InvalidOperation);
+                return false;
+            },
+            _ => {},
+        }
+
+        true
+    }
+
+    fn tex_image_2d(&self,
+                    target: u32,
+                    level: i32,
+                    internal_format: u32,
+                    width: i32,
+                    height: i32,
+                    border: i32,
+                    format: u32,
+                    data_type: u32,
+                    pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
+        // This should be validated before reaching this function
+        debug_assert!(self.validate_tex_image_parameters(target, level,
+                                                         internal_format,
+                                                         width, height,
+                                                         border, format,
+                                                         data_type));
+
+        let slot = match target {
+            constants::TEXTURE_2D
+                => self.bound_texture_2d.get(),
+            _   => self.bound_texture_cube_map.get(),
+        };
+
+        let texture = slot.as_ref().expect("No bound texture found after validation");
+
+        if format == constants::RGBA &&
+           data_type == constants::UNSIGNED_BYTE &&
+           self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) {
+            // TODO(emilio): premultiply here.
+        }
+
+        // TODO(emilio): Flip Y axis if necessary here
+
+        // TexImage2D depth is always equal to 1
+        handle_potential_webgl_error!(self, texture.initialize(target,
+                                                               width as u32,
+                                                               height as u32, 1,
+                                                               internal_format,
+                                                               level as u32));
+
+
+        // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
+        let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
+                                           width, height, format, data_type, pixels);
+
+        self.ipc_renderer
+            .send(CanvasMsg::WebGL(msg))
+            .unwrap()
     }
 }
 
 impl Drop for WebGLRenderingContext {
     fn drop(&mut self) {
         self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Close)).unwrap();
     }
 }
@@ -478,16 +611,17 @@ impl WebGLRenderingContextMethods for We
         };
 
         if let Some(buffer) = buffer {
             match buffer.bind(target) {
                 Ok(_) => slot.set(Some(buffer)),
                 Err(e) => return self.webgl_error(e),
             }
         } else {
+            slot.set(None);
             // Unbind the current buffer
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::BindBuffer(target, 0)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
@@ -521,17 +655,16 @@ impl WebGLRenderingContextMethods for We
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
         let slot = match target {
             constants::TEXTURE_2D => &self.bound_texture_2d,
             constants::TEXTURE_CUBE_MAP => &self.bound_texture_cube_map,
-
             _ => return self.webgl_error(InvalidEnum),
         };
 
         if let Some(texture) = texture {
             match texture.bind(target) {
                 Ok(_) => slot.set(Some(texture)),
                 Err(err) => return self.webgl_error(err),
             }
@@ -561,30 +694,34 @@ impl WebGLRenderingContextMethods for We
     #[allow(unsafe_code)]
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
     fn BufferData(&self, _cx: *mut JSContext, target: u32, data: Option<*mut JSObject>, usage: u32) {
         let bound_buffer = match target {
             constants::ARRAY_BUFFER => self.bound_buffer_array.get(),
             constants::ELEMENT_ARRAY_BUFFER => self.bound_buffer_element_array.get(),
             _ => return self.webgl_error(InvalidEnum),
         };
+
         let bound_buffer = match bound_buffer {
             Some(bound_buffer) => bound_buffer,
             None => return self.webgl_error(InvalidValue),
         };
+
         match usage {
             constants::STREAM_DRAW |
             constants::STATIC_DRAW |
             constants::DYNAMIC_DRAW => (),
             _ => return self.webgl_error(InvalidEnum),
         }
+
         let data = match data {
             Some(data) => data,
             None => return self.webgl_error(InvalidValue),
         };
+
         if let Some(data_vec) = array_buffer_view_to_vec::<u8>(data) {
             handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data_vec, usage));
         } else {
             // NB: array_buffer_view_to_vec should never fail when
             // we have WebIDL support for Float32Array etc.
             self.webgl_error(InvalidValue);
         }
     }
@@ -1104,199 +1241,221 @@ impl WebGLRenderingContextMethods for We
             None
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform1f(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   val: f32) {
-        if self.validate_uniform_parameters(uniform, UniformType::Float, Some(&[val])) {
+        if self.validate_uniform_parameters(uniform, UniformSetterType::Float, Some(&[val])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform1f(uniform.unwrap().id(), val)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform1i(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   val: i32) {
-        if self.validate_uniform_parameters(uniform, UniformType::Int, Some(&[val])) {
+        if self.validate_uniform_parameters(uniform, UniformSetterType::Int, Some(&[val])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform1i(uniform.unwrap().id(), val)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform1iv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::Int, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform, UniformSetterType::Int, data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform1iv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform1fv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::Float, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform, UniformSetterType::Float, data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform1fv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform2f(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   x: f32, y: f32) {
-        if self.validate_uniform_parameters(uniform, UniformType::FloatVec2, Some(&[x, y])) {
+        if self.validate_uniform_parameters(uniform, UniformSetterType::FloatVec2, Some(&[x, y])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform2f(uniform.unwrap().id(), x, y)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform2fv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::FloatVec2, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::FloatVec2,
+                                            data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform2fv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform2i(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   x: i32, y: i32) {
-        if self.validate_uniform_parameters(uniform, UniformType::IntVec2, Some(&[x, y])) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::IntVec2,
+                                            Some(&[x, y])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform2i(uniform.unwrap().id(), x, y)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform2iv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::IntVec2, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::IntVec2,
+                                            data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform2iv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform3f(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   x: f32, y: f32, z: f32) {
-        if self.validate_uniform_parameters(uniform, UniformType::FloatVec3, Some(&[x, y, z])) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::FloatVec3,
+                                            Some(&[x, y, z])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform3f(uniform.unwrap().id(), x, y, z)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform3fv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::FloatVec3, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::FloatVec3,
+                                            data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform3fv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform3i(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   x: i32, y: i32, z: i32) {
-        if self.validate_uniform_parameters(uniform, UniformType::IntVec3, Some(&[x, y, z])) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::IntVec3,
+                                            Some(&[x, y, z])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform3i(uniform.unwrap().id(), x, y, z)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform3iv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::IntVec3, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::IntVec3,
+                                            data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform3iv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform4i(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   x: i32, y: i32, z: i32, w: i32) {
-        if self.validate_uniform_parameters(uniform, UniformType::IntVec4, Some(&[x, y, z, w])) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::IntVec4,
+                                            Some(&[x, y, z, w])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform4i(uniform.unwrap().id(), x, y, z, w)))
                 .unwrap()
         }
     }
 
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform4iv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::IntVec4, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::IntVec4,
+                                            data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform4iv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform4f(&self,
                   uniform: Option<&WebGLUniformLocation>,
                   x: f32, y: f32, z: f32, w: f32) {
-        if self.validate_uniform_parameters(uniform, UniformType::FloatVec4, Some(&[x, y, z, w])) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::FloatVec4,
+                                            Some(&[x, y, z, w])) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform4f(uniform.unwrap().id(), x, y, z, w)))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn Uniform4fv(&self,
                   _cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: Option<*mut JSObject>) {
         let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
-        if self.validate_uniform_parameters(uniform, UniformType::FloatVec4, data_vec.as_ref().map(Vec::as_slice)) {
+        if self.validate_uniform_parameters(uniform,
+                                            UniformSetterType::FloatVec4,
+                                            data_vec.as_ref().map(Vec::as_slice)) {
             self.ipc_renderer
                 .send(CanvasMsg::WebGL(WebGLCommand::Uniform4fv(uniform.unwrap().id(), data_vec.unwrap())))
                 .unwrap()
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn UseProgram(&self, program: Option<&WebGLProgram>) {
@@ -1395,38 +1554,122 @@ impl WebGLRenderingContextMethods for We
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4
     fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
         self.ipc_renderer
             .send(CanvasMsg::WebGL(WebGLCommand::Viewport(x, y, width, height)))
             .unwrap()
     }
 
+    #[allow(unsafe_code)]
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     fn TexImage2D(&self,
+                  _cx: *mut JSContext,
                   target: u32,
                   level: i32,
                   internal_format: u32,
+                  width: i32,
+                  height: i32,
+                  border: i32,
                   format: u32,
                   data_type: u32,
-                  source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
-        let texture = match target {
-            constants::TEXTURE_2D => self.bound_texture_2d.get(),
-            constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(),
-            _ => return self.webgl_error(InvalidEnum),
+                  data: Option<*mut JSObject>) {
+        if !self.validate_tex_image_parameters(target,
+                                               level,
+                                               internal_format,
+                                               width, height,
+                                               border,
+                                               format,
+                                               data_type) {
+            return; // Error handled in validate()
+        }
+
+        // TODO(emilio, #10693): Add type-safe wrappers to validations
+        let (element_size, components_per_element) = match data_type {
+            constants::UNSIGNED_BYTE => (1, 1),
+            constants::UNSIGNED_SHORT_5_6_5 => (2, 3),
+            constants::UNSIGNED_SHORT_5_5_5_1 |
+            constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4),
+            _ => unreachable!(), // previously validated
         };
-        if texture.is_none() {
+
+        let components = match format {
+            constants::DEPTH_COMPONENT => 1,
+            constants::ALPHA => 1,
+            constants::LUMINANCE => 1,
+            constants::LUMINANCE_ALPHA => 2,
+            constants::RGB => 3,
+            constants::RGBA => 4,
+            _ => unreachable!(), // previously validated
+        };
+
+        // If data is non-null, the type of pixels must match the type of the
+        // data to be read.
+        // If it is UNSIGNED_BYTE, a Uint8Array must be supplied;
+        // if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4,
+        // or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied.
+        // If the types do not match, an INVALID_OPERATION error is generated.
+        let received_size = if let Some(data) = data {
+            if unsafe { array_buffer_view_data_checked::<u16>(data).is_some() } {
+                2
+            } else if unsafe { array_buffer_view_data_checked::<u8>(data).is_some() } {
+                1
+            } else {
+                return self.webgl_error(InvalidOperation);
+            }
+        } else {
+            element_size
+        };
+
+        if received_size != element_size {
             return self.webgl_error(InvalidOperation);
         }
-        // TODO(emilio): Validate more parameters
+
+        // NOTE: width and height are positive or zero due to validate()
+        let expected_byte_length = width * height * element_size * components / components_per_element;
+
+
+        // If data is null, a buffer of sufficient size
+        // initialized to 0 is passed.
+        let buff = if let Some(data) = data {
+            array_buffer_view_to_vec::<u8>(data)
+                .expect("Can't reach here without being an ArrayBufferView!")
+        } else {
+            vec![0u8; expected_byte_length as usize]
+        };
+
+        if buff.len() != expected_byte_length as usize {
+            return self.webgl_error(InvalidOperation);
+        }
+
+        self.tex_image_2d(target, level,
+                          internal_format,
+                          width, height, border,
+                          format, data_type, buff)
+    }
+
+    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
+    fn TexImage2D_(&self,
+                   target: u32,
+                   level: i32,
+                   internal_format: u32,
+                   format: u32,
+                   data_type: u32,
+                   source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
         let source = match source {
             Some(s) => s,
             None => return,
         };
 
+
+        // NOTE: Getting the pixels probably can be short-circuited if some
+        // parameter is invalid.
+        //
+        // Nontheless, since it's the error case, I'm not totally sure the
+        // complexity is worth it.
         let (pixels, size) = match source {
             ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => {
                 let global = self.global();
                 (image_data.get_data_array(&global.r()), image_data.get_size())
             },
             ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => {
                 let img_url = match image.get_url() {
                     Some(url) => url,
@@ -1438,61 +1681,57 @@ impl WebGLRenderingContextMethods for We
                 let img = match canvas_utils::request_image_from_cache(window.r(), img_url) {
                     ImageResponse::Loaded(img) => img,
                     ImageResponse::PlaceholderLoaded(_) | ImageResponse::None |
                     ImageResponse::MetadataLoaded(_)
                         => return,
                 };
 
                 let size = Size2D::new(img.width as i32, img.height as i32);
-                // TODO(emilio): Validate that the format argument is coherent with the image.
+
+                // TODO(emilio): Validate that the format argument
+                // is coherent with the image.
+                //
                 // RGB8 should be easy to support too
                 let mut data = match img.format {
                     PixelFormat::RGBA8 => img.bytes.to_vec(),
                     _ => unimplemented!(),
                 };
 
                 byte_swap(&mut data);
 
                 (data, size)
             },
-            // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, but
-            // we need to refactor it moving it to `HTMLCanvasElement` and supporting WebGLContext
+            // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
+            // but we need to refactor it moving it to `HTMLCanvasElement` and support
+            // WebGLContext (probably via GetPixels()).
             ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => {
                 let canvas = canvas.r();
                 if let Some((mut data, size)) = canvas.fetch_all_data() {
                     byte_swap(&mut data);
                     (data, size)
                 } else {
                     return
                 }
             },
             ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video)
                 => unimplemented!(),
         };
 
-        if size.width < 0 || size.height < 0 || level < 0 {
-            self.webgl_error(WebGLError::InvalidOperation);
+        // NB: Border must be zero
+        if !self.validate_tex_image_parameters(target, level, internal_format,
+                                               size.width, size.height, 0,
+                                               format, data_type) {
+            return; // Error handled in validate()
         }
 
-        // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
-        let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
-                                             size.width, size.height,
-                                             format, data_type, pixels);
-
-        // depth is always 1 when coming from html elements
-        handle_potential_webgl_error!(self, texture.unwrap().initialize(size.width as u32,
-                                                                        size.height as u32,
-                                                                        1,
-                                                                        internal_format,
-                                                                        level as u32));
-
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(msg))
-            .unwrap()
+        self.tex_image_2d(target, level,
+                          internal_format,
+                          size.width, size.height, 0,
+                          format, data_type, pixels)
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     fn TexParameterf(&self, target: u32, name: u32, value: f32) {
         self.tex_parameter(target, name, TexParameterValue::Float(value))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
@@ -1507,8 +1746,67 @@ pub trait LayoutCanvasWebGLRenderingCont
 }
 
 impl LayoutCanvasWebGLRenderingContextHelpers for LayoutJS<WebGLRenderingContext> {
     #[allow(unsafe_code)]
     unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
         (*self.unsafe_get()).ipc_renderer.clone()
     }
 }
+
+#[derive(Debug, PartialEq)]
+pub enum UniformSetterType {
+    Int,
+    IntVec2,
+    IntVec3,
+    IntVec4,
+    Float,
+    FloatVec2,
+    FloatVec3,
+    FloatVec4,
+}
+
+impl UniformSetterType {
+    pub fn element_count(&self) -> usize {
+        match *self {
+            UniformSetterType::Int => 1,
+            UniformSetterType::IntVec2 => 2,
+            UniformSetterType::IntVec3 => 3,
+            UniformSetterType::IntVec4 => 4,
+            UniformSetterType::Float => 1,
+            UniformSetterType::FloatVec2 => 2,
+            UniformSetterType::FloatVec3 => 3,
+            UniformSetterType::FloatVec4 => 4,
+        }
+    }
+
+    pub fn is_compatible_with(&self, gl_type: u32) -> bool {
+        gl_type == self.as_gl_constant() || match *self {
+            // Sampler uniform variables have an index value (the index of the
+            // texture), and as such they have to be set as ints
+            UniformSetterType::Int => gl_type == constants::SAMPLER_2D ||
+                                gl_type == constants::SAMPLER_CUBE,
+            // Don't ask me why, but it seems we must allow setting bool
+            // uniforms with uniform1f.
+            //
+            // See the WebGL conformance test
+            //   conformance/uniforms/gl-uniform-bool.html
+            UniformSetterType::Float => gl_type == constants::BOOL,
+            UniformSetterType::FloatVec2 => gl_type == constants::BOOL_VEC2,
+            UniformSetterType::FloatVec3 => gl_type == constants::BOOL_VEC3,
+            UniformSetterType::FloatVec4 => gl_type == constants::BOOL_VEC4,
+            _ => false,
+        }
+    }
+
+    fn as_gl_constant(&self) -> u32 {
+        match *self {
+            UniformSetterType::Int => constants::INT,
+            UniformSetterType::IntVec2 => constants::INT_VEC2,
+            UniformSetterType::IntVec3 => constants::INT_VEC3,
+            UniformSetterType::IntVec4 => constants::INT_VEC4,
+            UniformSetterType::Float => constants::FLOAT,
+            UniformSetterType::FloatVec2 => constants::FLOAT_VEC2,
+            UniformSetterType::FloatVec3 => constants::FLOAT_VEC3,
+            UniformSetterType::FloatVec4 => constants::FLOAT_VEC4,
+        }
+    }
+}
--- a/servo/components/script/dom/webglshader.rs
+++ b/servo/components/script/dom/webglshader.rs
@@ -111,16 +111,21 @@ impl WebGLShader {
                 },
                 Err(error) => {
                     self.compilation_status.set(ShaderCompilationStatus::Failed);
                     debug!("Shader {} compilation failed: {}", self.id, error);
                 },
             }
 
             *self.info_log.borrow_mut() = Some(validator.info_log());
+            // TODO(emilio): More data (like uniform data) should be collected
+            // here to properly validate uniforms.
+            //
+            // This requires a more complex interface with ANGLE, using C++
+            // bindings and being extremely cautious about destructing things.
         }
     }
 
     /// Mark this shader as deleted (if it wasn't previously)
     /// and delete it as if calling glDeleteShader.
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
--- a/servo/components/script/dom/webgltexture.rs
+++ b/servo/components/script/dom/webgltexture.rs
@@ -28,17 +28,16 @@ no_jsmanaged_fields!([ImageInfo; MAX_LEV
 
 #[dom_struct]
 pub struct WebGLTexture {
     webgl_object: WebGLObject,
     id: u32,
     /// The target to which this texture was bound the first time
     target: Cell<Option<u32>>,
     is_deleted: Cell<bool>,
-    is_initialized: Cell<bool>,
     /// Stores information about mipmap levels and cubemap faces.
     #[ignore_heap_size_of = "Arrays are cumbersome"]
     image_info_array: DOMRefCell<[ImageInfo; MAX_LEVEL_COUNT * MAX_FACE_COUNT]>,
     /// Face count can only be 1 or 6
     face_count: Cell<u8>,
     base_mipmap_level: u32,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
     renderer: IpcSender<CanvasMsg>,
@@ -46,17 +45,16 @@ pub struct WebGLTexture {
 
 impl WebGLTexture {
     fn new_inherited(renderer: IpcSender<CanvasMsg>, id: u32) -> WebGLTexture {
         WebGLTexture {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             target: Cell::new(None),
             is_deleted: Cell::new(false),
-            is_initialized: Cell::new(false),
             face_count: Cell::new(0),
             base_mipmap_level: 0,
             image_info_array: DOMRefCell::new([ImageInfo::new(); MAX_LEVEL_COUNT * MAX_FACE_COUNT]),
             renderer: renderer,
         }
     }
 
     pub fn maybe_new(global: GlobalRef, renderer: IpcSender<CanvasMsg>)
@@ -100,47 +98,61 @@ impl WebGLTexture {
             self.target.set(Some(target));
         }
 
         self.renderer.send(CanvasMsg::WebGL(WebGLCommand::BindTexture(target, self.id))).unwrap();
 
         Ok(())
     }
 
-    pub fn initialize(&self, width: u32, height: u32, depth: u32, internal_format: u32, level: u32) -> WebGLResult<()> {
+    pub fn initialize(&self,
+                      target: u32,
+                      width: u32,
+                      height: u32,
+                      depth: u32,
+                      internal_format: u32,
+                      level: u32) -> WebGLResult<()> {
         let image_info = ImageInfo {
             width: width,
             height: height,
             depth: depth,
             internal_format: Some(internal_format),
             is_initialized: true,
         };
-        self.set_image_infos_at_level(level, image_info);
 
-        self.is_initialized.set(true);
+        let face = match target {
+            constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP_POSITIVE_X => 0,
+            constants::TEXTURE_CUBE_MAP_NEGATIVE_X => 1,
+            constants::TEXTURE_CUBE_MAP_POSITIVE_Y => 2,
+            constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => 3,
+            constants::TEXTURE_CUBE_MAP_POSITIVE_Z => 4,
+            constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => 5,
+            _ => unreachable!(),
+        };
 
+        self.set_image_infos_at_level_and_face(level, face, image_info);
         Ok(())
     }
 
     pub fn generate_mipmap(&self) -> WebGLResult<()> {
         let target = match self.target.get() {
             Some(target) => target,
             None => {
                 error!("Cannot generate mipmap on texture that has no target!");
                 return Err(WebGLError::InvalidOperation);
             }
         };
 
         let base_image_info = self.base_image_info().unwrap();
-
         if !base_image_info.is_initialized() {
             return Err(WebGLError::InvalidOperation);
         }
 
-        if target == constants::TEXTURE_CUBE_MAP && !self.is_cube_complete() {
+        let is_cubic = target == constants::TEXTURE_CUBE_MAP;
+        if is_cubic && !self.is_cube_complete() {
             return Err(WebGLError::InvalidOperation);
         }
 
         if !base_image_info.is_power_of_two() {
             return Err(WebGLError::InvalidOperation);
         }
 
         if base_image_info.is_compressed_format() {
@@ -257,16 +269,18 @@ impl WebGLTexture {
             };
 
             self.set_image_infos_at_level(level, image_info);
         }
         Ok(())
     }
 
     fn is_cube_complete(&self) -> bool {
+        debug_assert!(self.face_count.get() == 6);
+
         let image_info = self.base_image_info().unwrap();
         if !image_info.is_defined() {
             return false;
         }
 
         let ref_width = image_info.width;
         let ref_format = image_info.internal_format;
 
@@ -289,21 +303,26 @@ impl WebGLTexture {
 
     fn image_info_at_face(&self, face: u8, level: u32) -> ImageInfo {
         let pos = (level * self.face_count.get() as u32) + face as u32;
         self.image_info_array.borrow()[pos as usize]
     }
 
     fn set_image_infos_at_level(&self, level: u32, image_info: ImageInfo) {
         for face in 0..self.face_count.get() {
-            let pos = (level * self.face_count.get() as u32) + face as u32;
-            self.image_info_array.borrow_mut()[pos as usize] = image_info;
+            self.set_image_infos_at_level_and_face(level, face, image_info);
         }
     }
 
+    fn set_image_infos_at_level_and_face(&self, level: u32, face: u8, image_info: ImageInfo) {
+        debug_assert!(face < self.face_count.get());
+        let pos = (level * self.face_count.get() as u32) + face as u32;
+        self.image_info_array.borrow_mut()[pos as usize] = image_info;
+    }
+
     fn base_image_info(&self) -> Option<ImageInfo> {
         assert!((self.base_mipmap_level as usize) < MAX_LEVEL_COUNT);
 
         Some(self.image_info_at_face(0, self.base_mipmap_level))
     }
 }
 
 impl Drop for WebGLTexture {
@@ -336,17 +355,17 @@ impl ImageInfo {
         self.width.is_power_of_two() && self.height.is_power_of_two() && self.depth.is_power_of_two()
     }
 
     fn is_initialized(&self) -> bool {
         self.is_initialized
     }
 
     fn is_defined(&self) -> bool {
-        !self.internal_format.is_none()
+        self.internal_format.is_some()
     }
 
     fn get_max_mimap_levels(&self) -> u32 {
         let largest = cmp::max(cmp::max(self.width, self.height), self.depth);
         if largest == 0 {
             return 0;
         }
         // FloorLog2(largest) + 1
--- a/servo/components/script/dom/webgluniformlocation.rs
+++ b/servo/components/script/dom/webgluniformlocation.rs
@@ -23,20 +23,17 @@ impl WebGLUniformLocation {
             program_id: program_id,
         }
     }
 
     pub fn new(global: GlobalRef, id: i32, program_id: u32) -> Root<WebGLUniformLocation> {
         reflect_dom_object(
             box WebGLUniformLocation::new_inherited(id, program_id), global, WebGLUniformLocationBinding::Wrap)
     }
-}
 
-
-impl WebGLUniformLocation {
     pub fn id(&self) -> i32 {
         self.id
     }
 
     pub fn program_id(&self) -> u32 {
         self.program_id
     }
 }
--- a/servo/components/script/dom/webidls/WebGLRenderingContext.webidl
+++ b/servo/components/script/dom/webidls/WebGLRenderingContext.webidl
@@ -627,16 +627,20 @@ interface WebGLRenderingContextBase
     //void stencilMask(GLuint mask);
     //void stencilMaskSeparate(GLenum face, GLuint mask);
     //void stencilOp(GLenum fail, GLenum zfail, GLenum zpass);
     //void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
 
     //void texImage2D(GLenum target, GLint level, GLenum internalformat,
     //                GLsizei width, GLsizei height, GLint border, GLenum format,
     //                GLenum type, ArrayBufferView? pixels);
+    // FIXME: SM interface arguments
+    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+                    GLsizei width, GLsizei height, GLint border, GLenum format,
+                    GLenum type, optional object data);
     void texImage2D(GLenum target, GLint level, GLenum internalformat,
                     GLenum format, GLenum type, TexImageSource? source); // May throw DOMException
 
     void texParameterf(GLenum target, GLenum pname, GLfloat param);
     void texParameteri(GLenum target, GLenum pname, GLint param);
 
     //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
     //                   GLsizei width, GLsizei height,