servo: Merge #17891 - Improved WebGL architecture (from MortimerGoro:webgl_move); r=glennw,emilio
authorImanol Fernandez <mortimergoro@gmail.com>
Tue, 15 Aug 2017 16:00:10 -0500
changeset 647083 67647a813c56b35e649963f945ecf4bc43771b59
parent 647082 7e4632a2b787ef5bddbf232c2de94449eb004d39
child 647084 05a5b7e96f58d7a25b47f4c96c2935233b2c45ed
push id74288
push userhikezoe@mozilla.com
push dateWed, 16 Aug 2017 00:19:57 +0000
reviewersglennw, emilio
milestone57.0a1
servo: Merge #17891 - Improved WebGL architecture (from MortimerGoro:webgl_move); r=glennw,emilio <!-- Please describe your changes on the following line: --> Info about the big picture and the goals of the WebGL refactor in this thread: https://groups.google.com/forum/#!topic/mozilla.dev.servo/0WMGz60kKzQ I tried to reduce this PR as much as possible as requested in the thread. I'll do separate PRs for other features (e.g.: Batch messages or use shared memory to improve frame times) or fixes. Some tips to ease the review process: - Most changes in DOM objects follow the same pattern (remove CanvasMsg wrapper and use the new sender method). - WebGLCommands are the same ones as before (moved from webrender_api). So those lines are already reviewed. - See WebGL traits in [components/canvas_traits/webgl.rs](https://github.com/servo/servo/pull/17891/files#diff-8701045d01505418701d0631d4d45562) - See WebGLThread and WR External Image bridge in [components/canvas/webgl_thread.rs](https://github.com/servo/servo/pull/17891/files#diff-281554879f39a2a041f7a69d442a5d2e) - The implementation submitted in this PR creates a single `WebGLThread` for all ScriptThread/Pipelines. See that in [components/canvas/webgl_mode/inprocess.rs](https://github.com/servo/servo/pull/17891/files#diff-250070c6c5a38c7f9fa0f5b3c101f68b) The conformance tests will help to guarantee that we don't miss anything. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 90f55ea4580e2a15f7d70d0491444f18b972d450
servo/Cargo.lock
servo/components/canvas/Cargo.toml
servo/components/canvas/canvas_paint_thread.rs
servo/components/canvas/gl_context.rs
servo/components/canvas/lib.rs
servo/components/canvas/webgl_mode/inprocess.rs
servo/components/canvas/webgl_mode/mod.rs
servo/components/canvas/webgl_paint_thread.rs
servo/components/canvas/webgl_thread.rs
servo/components/canvas_traits/Cargo.toml
servo/components/canvas_traits/canvas.rs
servo/components/canvas_traits/lib.rs
servo/components/canvas_traits/webgl.rs
servo/components/canvas_traits/webgl_channel/ipc.rs
servo/components/canvas_traits/webgl_channel/mod.rs
servo/components/canvas_traits/webgl_channel/mpsc.rs
servo/components/constellation/Cargo.toml
servo/components/constellation/constellation.rs
servo/components/constellation/lib.rs
servo/components/constellation/pipeline.rs
servo/components/gfx/display_list/mod.rs
servo/components/layout/display_list_builder.rs
servo/components/layout/fragment.rs
servo/components/layout/webrender_helpers.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/canvasgradient.rs
servo/components/script/dom/canvaspattern.rs
servo/components/script/dom/canvasrenderingcontext2d.rs
servo/components/script/dom/htmlcanvaselement.rs
servo/components/script/dom/paintrenderingcontext2d.rs
servo/components/script/dom/paintworkletglobalscope.rs
servo/components/script/dom/vrdisplay.rs
servo/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs
servo/components/script/dom/webgl_extensions/ext/webglvertexarrayobjectoes.rs
servo/components/script/dom/webgl_extensions/extensions.rs
servo/components/script/dom/webgl_validations/tex_image_2d.rs
servo/components/script/dom/webglbuffer.rs
servo/components/script/dom/webglframebuffer.rs
servo/components/script/dom/webglprogram.rs
servo/components/script/dom/webglrenderbuffer.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/window.rs
servo/components/script/script_thread.rs
servo/components/script_layout_interface/lib.rs
servo/components/script_traits/Cargo.toml
servo/components/script_traits/lib.rs
servo/components/script_traits/script_msg.rs
servo/components/servo/lib.rs
servo/components/webvr/Cargo.toml
servo/components/webvr/lib.rs
servo/components/webvr/webvr_thread.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -320,37 +320,41 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "canvas"
 version = "0.0.1"
 dependencies = [
  "azure 0.20.1 (git+https://github.com/servo/rust-azure)",
  "canvas_traits 0.0.1",
+ "compositing 0.0.1",
  "cssparser 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo_config 0.0.1",
+ "webrender 0.48.0 (git+https://github.com/servo/webrender)",
  "webrender_api 0.48.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "canvas_traits"
 version = "0.0.1"
 dependencies = [
  "cssparser 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "offscreen_gl_context 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_config 0.0.1",
  "webrender_api 0.48.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "caseless"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
@@ -521,17 +525,16 @@ dependencies = [
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "layout_traits 0.0.1",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "metrics 0.0.1",
  "msg 0.0.1",
  "net 0.0.1",
  "net_traits 0.0.1",
- "offscreen_gl_context 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "profile_traits 0.0.1",
  "script_traits 0.0.1",
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_config 0.0.1",
  "servo_rand 0.0.1",
  "servo_remutex 0.0.1",
  "servo_url 0.0.1",
  "style_traits 0.0.1",
@@ -2591,17 +2594,16 @@ dependencies = [
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper_serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "metrics 0.0.1",
  "msg 0.0.1",
  "net_traits 0.0.1",
- "offscreen_gl_context 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "profile_traits 0.0.1",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_atoms 0.0.1",
  "servo_url 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3524,22 +3526,23 @@ dependencies = [
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webvr"
 version = "0.0.1"
 dependencies = [
+ "canvas_traits 0.0.1",
+ "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "script_traits 0.0.1",
  "servo_config 0.0.1",
- "webrender_api 0.48.0 (git+https://github.com/servo/webrender)",
  "webvr_traits 0.0.1",
 ]
 
 [[package]]
 name = "winapi"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
--- a/servo/components/canvas/Cargo.toml
+++ b/servo/components/canvas/Cargo.toml
@@ -7,17 +7,18 @@ publish = false
 
 [lib]
 name = "canvas"
 path = "lib.rs"
 
 [dependencies]
 azure = {git = "https://github.com/servo/rust-azure"}
 canvas_traits = {path = "../canvas_traits"}
+compositing = {path = "../compositing"}
 cssparser = "0.19"
 euclid = "0.15"
 gleam = "0.4"
 ipc-channel = "0.8"
 log = "0.3.5"
 num-traits = "0.1.32"
 offscreen_gl_context = { version = "0.11", features = ["serde"] }
-servo_config = {path = "../config"}
+webrender = {git = "https://github.com/servo/webrender"}
 webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
--- a/servo/components/canvas/canvas_paint_thread.rs
+++ b/servo/components/canvas/canvas_paint_thread.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use azure::azure::AzFloat;
 use azure::azure_hl::{AntialiasMode, CapStyle, CompositionOp, JoinStyle};
 use azure::azure_hl::{BackendType, DrawOptions, DrawTarget, Pattern, StrokeOptions, SurfaceFormat};
 use azure::azure_hl::{Color, ColorPattern, DrawSurfaceOptions, Filter, PathBuilder};
 use azure::azure_hl::{ExtendMode, GradientStop, LinearGradientPattern, RadialGradientPattern};
 use azure::azure_hl::SurfacePattern;
-use canvas_traits::*;
+use canvas_traits::canvas::*;
 use cssparser::RGBA;
 use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D};
 use ipc_channel::ipc::{self, IpcSender};
 use num_traits::ToPrimitive;
 use std::borrow::ToOwned;
 use std::mem;
 use std::sync::Arc;
 use std::thread;
@@ -188,38 +188,32 @@ impl<'a> CanvasPaintThread<'a> {
                             Canvas2dMsg::PutImageData(imagedata, offset, image_data_size, dirty_rect)
                                 => painter.put_image_data(imagedata, offset, image_data_size, dirty_rect),
                             Canvas2dMsg::SetShadowOffsetX(value) => painter.set_shadow_offset_x(value),
                             Canvas2dMsg::SetShadowOffsetY(value) => painter.set_shadow_offset_y(value),
                             Canvas2dMsg::SetShadowBlur(value) => painter.set_shadow_blur(value),
                             Canvas2dMsg::SetShadowColor(ref color) => painter.set_shadow_color(color.to_azure_style()),
                         }
                     },
-                    CanvasMsg::Common(message) => {
-                        match message {
-                            CanvasCommonMsg::Close => break,
-                            CanvasCommonMsg::Recreate(size) => painter.recreate(size),
-                        }
-                    },
+                    CanvasMsg::Close => break,
+                    CanvasMsg::Recreate(size) => painter.recreate(size),
                     CanvasMsg::FromScript(message) => {
                         match message {
                             FromScriptMsg::SendPixels(chan) => {
                                 painter.send_pixels(chan)
                             }
                         }
                     }
                     CanvasMsg::FromLayout(message) => {
                         match message {
                             FromLayoutMsg::SendData(chan) => {
                                 painter.send_data(chan)
                             }
                         }
                     }
-                    CanvasMsg::WebGL(_) => panic!("Wrong WebGL message sent to Canvas2D thread"),
-                    CanvasMsg::WebVR(_) => panic!("Wrong WebVR message sent to Canvas2D thread"),
                 }
             }
         }).expect("Thread spawning failed");
 
         sender
     }
 
     fn save_context_state(&mut self) {
@@ -566,17 +560,17 @@ impl<'a> CanvasPaintThread<'a> {
     }
 
     fn send_pixels(&mut self, chan: IpcSender<Option<Vec<u8>>>) {
         self.drawtarget.snapshot().get_data_surface().with_data(|element| {
             chan.send(Some(element.into())).unwrap();
         })
     }
 
-    fn send_data(&mut self, chan: IpcSender<CanvasData>) {
+    fn send_data(&mut self, chan: IpcSender<CanvasImageData>) {
         self.drawtarget.snapshot().get_data_surface().with_data(|element| {
             let size = self.drawtarget.get_size();
 
             let descriptor = webrender_api::ImageDescriptor {
                 width: size.width as u32,
                 height: size.height as u32,
                 stride: None,
                 format: webrender_api::ImageFormat::BGRA8,
@@ -609,17 +603,17 @@ impl<'a> CanvasPaintThread<'a> {
                 updates.delete_image(image_key);
             }
 
             self.webrender_api.update_resources(updates);
 
             let data = CanvasImageData {
                 image_key: self.image_key.unwrap(),
             };
-            chan.send(CanvasData::Image(data)).unwrap();
+            chan.send(data).unwrap();
         })
     }
 
     fn image_data(&self, dest_rect: Rect<i32>, canvas_size: Size2D<f64>, chan: IpcSender<Vec<u8>>) {
         let mut dest_data = self.read_pixels(dest_rect, canvas_size);
 
         // bgra -> rgba
         byte_swap(&mut dest_data);
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas/gl_context.rs
@@ -0,0 +1,203 @@
+/* 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::webgl::WebGLCommand;
+use compositing::compositor_thread::{CompositorProxy, self};
+use euclid::Size2D;
+use gleam::gl;
+use offscreen_gl_context::{ColorAttachmentType, GLContext, GLContextAttributes, GLContextDispatcher, GLLimits};
+use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle, NativeGLContextMethods};
+use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle};
+use std::sync::{Arc, Mutex};
+use super::webgl_thread::WebGLImpl;
+
+/// The GLContextFactory is used to create shared GL contexts with the main thread GL context.
+/// Currently, shared textures are used to render WebGL textures into the WR compositor.
+/// In order to create a shared context, the GLContextFactory stores the handle of the main GL context.
+pub enum GLContextFactory {
+    Native(NativeGLContextHandle, Option<MainThreadDispatcher>),
+    OSMesa(OSMesaContextHandle),
+}
+
+impl GLContextFactory {
+    /// Creates a new GLContextFactory that uses the currently bound GL context to create shared contexts.
+    pub fn current_native_handle(proxy: &CompositorProxy) -> Option<GLContextFactory> {
+        NativeGLContext::current_handle().map(|handle| {
+            if cfg!(target_os = "windows") {
+                // Used to dispatch functions from the GLContext thread to the main thread's event loop.
+                // Required to allow WGL GLContext sharing in Windows.
+                GLContextFactory::Native(handle, Some(MainThreadDispatcher::new(proxy.clone_compositor_proxy())))
+            } else {
+                GLContextFactory::Native(handle, None)
+            }
+        })
+    }
+
+    /// Creates a new GLContextFactory that uses the currently bound OSMesa context to create shared contexts.
+    pub fn current_osmesa_handle() -> Option<GLContextFactory> {
+        OSMesaContext::current_handle().map(GLContextFactory::OSMesa)
+    }
+
+    /// Creates a new shared GLContext with the main GLContext
+    pub fn new_shared_context(&self,
+                              size: Size2D<i32>,
+                              attributes: GLContextAttributes) -> Result<GLContextWrapper, &'static str> {
+        match *self {
+            GLContextFactory::Native(ref handle, ref dispatcher) => {
+                let dispatcher = dispatcher.as_ref().map(|d| Box::new(d.clone()) as Box<_>);
+                let ctx = GLContext::<NativeGLContext>::new_shared_with_dispatcher(size,
+                                                                                   attributes,
+                                                                                   ColorAttachmentType::Texture,
+                                                                                   gl::GlType::default(),
+                                                                                   Some(handle),
+                                                                                   dispatcher);
+                ctx.map(GLContextWrapper::Native)
+            }
+            GLContextFactory::OSMesa(ref handle) => {
+                let ctx = GLContext::<OSMesaContext>::new_shared_with_dispatcher(size.to_untyped(),
+                                                                                 attributes,
+                                                                                 ColorAttachmentType::Texture,
+                                                                                 gl::GlType::default(),
+                                                                                 Some(handle),
+                                                                                 None);
+                ctx.map(GLContextWrapper::OSMesa)
+            }
+        }
+    }
+
+    /// Creates a new non-shared GLContext
+    pub fn new_context(&self,
+                       size: Size2D<i32>,
+                       attributes: GLContextAttributes) -> Result<GLContextWrapper, &'static str> {
+        match *self {
+            GLContextFactory::Native(..) => {
+                let ctx = GLContext::<NativeGLContext>::new_shared_with_dispatcher(size,
+                                                                                   attributes,
+                                                                                   ColorAttachmentType::Texture,
+                                                                                   gl::GlType::default(),
+                                                                                   None,
+                                                                                   None);
+                ctx.map(GLContextWrapper::Native)
+            }
+            GLContextFactory::OSMesa(_) => {
+                let ctx = GLContext::<OSMesaContext>::new_shared_with_dispatcher(size.to_untyped(),
+                                                                                 attributes,
+                                                                                 ColorAttachmentType::Texture,
+                                                                                 gl::GlType::default(),
+                                                                                 None,
+                                                                                 None);
+                ctx.map(GLContextWrapper::OSMesa)
+            }
+        }
+    }
+}
+
+
+/// GLContextWrapper used to abstract NativeGLContext and OSMesaContext types
+pub enum GLContextWrapper {
+    Native(GLContext<NativeGLContext>),
+    OSMesa(GLContext<OSMesaContext>),
+}
+
+impl GLContextWrapper {
+    pub fn make_current(&self) {
+        match *self {
+            GLContextWrapper::Native(ref ctx) => {
+                ctx.make_current().unwrap();
+            }
+            GLContextWrapper::OSMesa(ref ctx) => {
+                ctx.make_current().unwrap();
+            }
+        }
+    }
+
+    pub fn unbind(&self) {
+        match *self {
+            GLContextWrapper::Native(ref ctx) => {
+                ctx.unbind().unwrap();
+            }
+            GLContextWrapper::OSMesa(ref ctx) => {
+                ctx.unbind().unwrap();
+            }
+        }
+    }
+
+    pub fn apply_command(&self, cmd: WebGLCommand) {
+        match *self {
+            GLContextWrapper::Native(ref ctx) => {
+                WebGLImpl::apply(ctx, cmd);
+            }
+            GLContextWrapper::OSMesa(ref ctx) => {
+                WebGLImpl::apply(ctx, cmd);
+            }
+        }
+    }
+
+    pub fn gl(&self) -> &gl::Gl {
+        match *self {
+            GLContextWrapper::Native(ref ctx) => {
+                ctx.gl()
+            }
+            GLContextWrapper::OSMesa(ref ctx) => {
+                ctx.gl()
+            }
+        }
+    }
+
+    pub fn get_info(&self) -> (Size2D<i32>, u32, GLLimits) {
+        match *self {
+            GLContextWrapper::Native(ref ctx) => {
+                let (real_size, texture_id) = {
+                    let draw_buffer = ctx.borrow_draw_buffer().unwrap();
+                    (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
+                };
+
+                let limits = ctx.borrow_limits().clone();
+
+                (real_size, texture_id, limits)
+            }
+            GLContextWrapper::OSMesa(ref ctx) => {
+                let (real_size, texture_id) = {
+                    let draw_buffer = ctx.borrow_draw_buffer().unwrap();
+                    (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
+                };
+
+                let limits = ctx.borrow_limits().clone();
+
+                (real_size, texture_id, limits)
+            }
+        }
+    }
+
+    pub fn resize(&mut self, size: Size2D<i32>) -> Result<(), &'static str> {
+        match *self {
+            GLContextWrapper::Native(ref mut ctx) => {
+                ctx.resize(size)
+            }
+            GLContextWrapper::OSMesa(ref mut ctx) => {
+                ctx.resize(size)
+            }
+        }
+    }
+}
+
+/// Implements GLContextDispatcher to dispatch functions from GLContext threads to the main thread's event loop.
+/// It's used in Windows to allow WGL GLContext sharing.
+#[derive(Clone)]
+pub struct MainThreadDispatcher {
+    compositor_proxy: Arc<Mutex<CompositorProxy>>
+}
+
+impl MainThreadDispatcher {
+    fn new(proxy: CompositorProxy) -> Self {
+        Self {
+            compositor_proxy: Arc::new(Mutex::new(proxy)),
+        }
+    }
+}
+impl GLContextDispatcher for MainThreadDispatcher {
+    fn dispatch(&self, f: Box<Fn() + Send>) {
+        self.compositor_proxy.lock().unwrap().send(compositor_thread::Msg::Dispatch(f));
+    }
+}
--- a/servo/components/canvas/lib.rs
+++ b/servo/components/canvas/lib.rs
@@ -1,21 +1,23 @@
 /* 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/. */
 
 #![deny(unsafe_code)]
 
 extern crate azure;
 extern crate canvas_traits;
+extern crate compositing;
 extern crate cssparser;
 extern crate euclid;
 extern crate gleam;
 extern crate ipc_channel;
-#[macro_use]
-extern crate log;
+#[macro_use] extern crate log;
 extern crate num_traits;
 extern crate offscreen_gl_context;
-extern crate servo_config;
+extern crate webrender;
 extern crate webrender_api;
 
 pub mod canvas_paint_thread;
-pub mod webgl_paint_thread;
+pub mod gl_context;
+mod webgl_mode;
+pub mod webgl_thread;
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas/webgl_mode/inprocess.rs
@@ -0,0 +1,95 @@
+/* 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 ::gl_context::GLContextFactory;
+use ::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThreadObserver, WebGLThread};
+use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
+use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
+use canvas_traits::webgl::webgl_channel;
+use euclid::Size2D;
+use std::marker::PhantomData;
+use webrender;
+use webrender_api;
+
+/// WebGL Threading API entry point that lives in the constellation.
+pub struct WebGLThreads(WebGLSender<WebGLMsg>);
+
+impl WebGLThreads {
+    /// Creates a new WebGLThreads object
+    pub fn new(gl_factory: GLContextFactory,
+               webrender_api_sender: webrender_api::RenderApiSender,
+               webvr_compositor: Option<Box<WebVRRenderHandler>>)
+               -> (WebGLThreads, Box<webrender::ExternalImageHandler>) {
+        // This implementation creates a single `WebGLThread` for all the pipelines.
+        let channel = WebGLThread::start(gl_factory,
+                                         webrender_api_sender,
+                                         webvr_compositor.map(|c| WebVRRenderWrapper(c)),
+                                         PhantomData);
+        let external = WebGLExternalImageHandler::new(WebGLExternalImages::new(channel.clone()));
+        (WebGLThreads(channel), Box::new(external))
+    }
+
+    /// Gets the WebGLThread handle for each script pipeline.
+    pub fn pipeline(&self) -> WebGLPipeline {
+        // This mode creates a single thread, so the existing WebGLChan is just cloned.
+        WebGLPipeline(WebGLChan(self.0.clone()))
+    }
+
+    /// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
+    pub fn exit(&self) -> Result<(), &'static str> {
+        self.0.send(WebGLMsg::Exit).map_err(|_| "Failed to send Exit message")
+    }
+}
+
+/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
+struct WebGLExternalImages {
+    webgl_channel: WebGLSender<WebGLMsg>,
+    // Used to avoid creating a new channel on each received WebRender request.
+    lock_channel: (WebGLSender<(u32, Size2D<i32>)>, WebGLReceiver<(u32, Size2D<i32>)>),
+}
+
+impl WebGLExternalImages {
+    fn new(channel: WebGLSender<WebGLMsg>) -> Self {
+        Self {
+            webgl_channel: channel,
+            lock_channel: webgl_channel().unwrap(),
+        }
+    }
+}
+
+impl WebGLExternalImageApi for WebGLExternalImages {
+    fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) {
+        self.webgl_channel.send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone())).unwrap();
+        self.lock_channel.1.recv().unwrap()
+    }
+
+    fn unlock(&mut self, ctx_id: WebGLContextId) {
+        self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap();
+    }
+}
+
+/// Custom observer used in a `WebGLThread`.
+impl WebGLThreadObserver for PhantomData<()> {
+    fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) {
+        debug!("WebGLContext created (ctx_id: {:?} texture_id: {:?} size: {:?}", ctx_id, texture_id, size);
+    }
+
+    fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) {
+        debug!("WebGLContext resized (ctx_id: {:?} texture_id: {:?} size: {:?}", ctx_id, texture_id, size);
+    }
+
+    fn on_context_delete(&mut self, ctx_id: WebGLContextId) {
+        debug!("WebGLContext deleted (ctx_id: {:?})", ctx_id);
+    }
+}
+
+
+/// Wrapper to send WebVR commands used in `WebGLThread`.
+struct WebVRRenderWrapper(Box<WebVRRenderHandler>);
+
+impl WebVRRenderHandler for WebVRRenderWrapper {
+    fn handle(&mut self, command: WebVRCommand, texture: Option<(u32, Size2D<i32>)>) {
+        self.0.handle(command, texture);
+    }
+}
copy from servo/tests/unit/style/rule_tree/mod.rs
copy to servo/components/canvas/webgl_mode/mod.rs
--- a/servo/tests/unit/style/rule_tree/mod.rs
+++ b/servo/components/canvas/webgl_mode/mod.rs
@@ -1,5 +1,6 @@
 /* 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/. */
 
-mod bench;
+mod inprocess;
+pub use self::inprocess::WebGLThreads;
deleted file mode 100644
--- a/servo/components/canvas/webgl_paint_thread.rs
+++ /dev/null
@@ -1,379 +0,0 @@
-/* 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, CanvasData, CanvasMsg, CanvasImageData};
-use canvas_traits::{FromLayoutMsg, FromScriptMsg, byte_swap};
-use euclid::Size2D;
-use gleam::gl;
-use ipc_channel::ipc::{self, IpcSender};
-use offscreen_gl_context::{ColorAttachmentType, GLContext, GLLimits};
-use offscreen_gl_context::{GLContextAttributes, NativeGLContext, OSMesaContext};
-use servo_config::opts;
-use std::borrow::ToOwned;
-use std::mem;
-use std::sync::Arc;
-use std::sync::mpsc::channel;
-use std::thread;
-use webrender_api;
-
-enum GLContextWrapper {
-    Native(GLContext<NativeGLContext>),
-    OSMesa(GLContext<OSMesaContext>),
-}
-
-impl GLContextWrapper {
-    fn new(size: Size2D<i32>,
-           attributes: GLContextAttributes,
-           gl_type: gl::GlType) -> Result<GLContextWrapper, &'static str> {
-        if opts::get().should_use_osmesa() {
-            let ctx = GLContext::<OSMesaContext>::new(size,
-                                                      attributes,
-                                                      ColorAttachmentType::Texture,
-                                                      gl_type,
-                                                      None);
-            ctx.map(GLContextWrapper::OSMesa)
-        } else {
-            let ctx = GLContext::<NativeGLContext>::new(size,
-                                                        attributes,
-                                                        ColorAttachmentType::Texture,
-                                                        gl_type,
-                                                        None);
-            ctx.map(GLContextWrapper::Native)
-        }
-    }
-
-    pub fn get_limits(&self) -> GLLimits {
-        match *self {
-            GLContextWrapper::Native(ref ctx) => {
-                ctx.borrow_limits().clone()
-            }
-            GLContextWrapper::OSMesa(ref ctx) => {
-                ctx.borrow_limits().clone()
-            }
-        }
-    }
-
-    fn resize(&mut self, size: Size2D<i32>) -> Result<Size2D<i32>, &'static str> {
-        match *self {
-            GLContextWrapper::Native(ref mut ctx) => {
-                ctx.resize(size)?;
-                Ok(ctx.borrow_draw_buffer().unwrap().size())
-            }
-            GLContextWrapper::OSMesa(ref mut ctx) => {
-                ctx.resize(size)?;
-                Ok(ctx.borrow_draw_buffer().unwrap().size())
-            }
-        }
-    }
-
-    fn gl(&self) -> &gl::Gl {
-        match *self {
-            GLContextWrapper::Native(ref ctx) => {
-                ctx.gl()
-            }
-            GLContextWrapper::OSMesa(ref ctx) => {
-                ctx.gl()
-            }
-        }
-    }
-
-    pub fn make_current(&self) {
-        match *self {
-            GLContextWrapper::Native(ref ctx) => {
-                ctx.make_current().unwrap();
-            }
-            GLContextWrapper::OSMesa(ref ctx) => {
-                ctx.make_current().unwrap();
-            }
-        }
-    }
-
-    pub fn apply_command(&self, cmd: webrender_api::WebGLCommand) {
-        match *self {
-            GLContextWrapper::Native(ref ctx) => {
-                cmd.apply(ctx);
-            }
-            GLContextWrapper::OSMesa(ref ctx) => {
-                cmd.apply(ctx);
-            }
-        }
-    }
-}
-
-enum WebGLPaintTaskData {
-    WebRender(webrender_api::RenderApi, webrender_api::WebGLContextId),
-    Readback {
-        context: GLContextWrapper,
-        webrender_api: webrender_api::RenderApi,
-        image_key: Option<webrender_api::ImageKey>,
-        /// An old webrender image key that can be deleted when the next epoch ends.
-        old_image_key: Option<webrender_api::ImageKey>,
-        /// An old webrender image key that can be deleted when the current epoch ends.
-        very_old_image_key: Option<webrender_api::ImageKey>,
-    },
-}
-
-pub struct WebGLPaintThread {
-    size: Size2D<i32>,
-    data: WebGLPaintTaskData,
-}
-
-fn create_readback_painter(size: Size2D<i32>,
-                           attrs: GLContextAttributes,
-                           webrender_api: webrender_api::RenderApi,
-                           gl_type: gl::GlType)
-    -> Result<(WebGLPaintThread, GLLimits), String> {
-    let context = GLContextWrapper::new(size, attrs, gl_type)?;
-    let limits = context.get_limits();
-    let painter = WebGLPaintThread {
-        size: size,
-        data: WebGLPaintTaskData::Readback {
-            context: context,
-            webrender_api: webrender_api,
-            image_key: None,
-            old_image_key: None,
-            very_old_image_key: None,
-        },
-    };
-
-    Ok((painter, limits))
-}
-
-impl WebGLPaintThread {
-    fn new(size: Size2D<i32>,
-           attrs: GLContextAttributes,
-           webrender_api_sender: webrender_api::RenderApiSender,
-           gl_type: gl::GlType)
-        -> Result<(WebGLPaintThread, GLLimits), String> {
-        let wr_api = webrender_api_sender.create_api();
-        let device_size = webrender_api::DeviceIntSize::from_untyped(&size);
-        match wr_api.request_webgl_context(&device_size, attrs) {
-            Ok((id, limits)) => {
-                let painter = WebGLPaintThread {
-                    data: WebGLPaintTaskData::WebRender(wr_api, id),
-                    size: size
-                };
-                Ok((painter, limits))
-            },
-            Err(msg) => {
-                warn!("Initial context creation failed, falling back to readback: {}", msg);
-                create_readback_painter(size, attrs, wr_api, gl_type)
-            }
-        }
-    }
-
-    fn handle_webgl_message(&self, message: webrender_api::WebGLCommand) {
-        debug!("WebGL message: {:?}", message);
-        match self.data {
-            WebGLPaintTaskData::WebRender(ref api, id) => {
-                api.send_webgl_command(id, message);
-            }
-            WebGLPaintTaskData::Readback { ref context, .. } => {
-                context.apply_command(message);
-            }
-        }
-    }
-
-    fn handle_webvr_message(&self, message: webrender_api::VRCompositorCommand) {
-        match self.data {
-            WebGLPaintTaskData::WebRender(ref api, id) => {
-                api.send_vr_compositor_command(id, message);
-            }
-            WebGLPaintTaskData::Readback { .. } => {
-                error!("Webrender is required for WebVR implementation");
-            }
-        }
-    }
-
-
-    /// Creates a new `WebGLPaintThread` and returns an `IpcSender` to
-    /// communicate with it.
-    pub fn start(size: Size2D<i32>,
-                 attrs: GLContextAttributes,
-                 webrender_api_sender: webrender_api::RenderApiSender)
-                 -> Result<(IpcSender<CanvasMsg>, GLLimits), String> {
-        let (sender, receiver) = ipc::channel::<CanvasMsg>().unwrap();
-        let (result_chan, result_port) = channel();
-        thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || {
-            let gl_type = gl::GlType::default();
-            let mut painter = match WebGLPaintThread::new(size, attrs, webrender_api_sender, gl_type) {
-                Ok((thread, limits)) => {
-                    result_chan.send(Ok(limits)).unwrap();
-                    thread
-                },
-                Err(e) => {
-                    result_chan.send(Err(e)).unwrap();
-                    return
-                }
-            };
-            painter.init();
-            loop {
-                match receiver.recv().unwrap() {
-                    CanvasMsg::WebGL(message) => painter.handle_webgl_message(message),
-                    CanvasMsg::Common(message) => {
-                        match message {
-                            CanvasCommonMsg::Close => break,
-                            // TODO(emilio): handle error nicely
-                            CanvasCommonMsg::Recreate(size) => painter.recreate(size).unwrap(),
-                        }
-                    },
-                    CanvasMsg::FromScript(message) => {
-                        match message {
-                            FromScriptMsg::SendPixels(chan) =>{
-                                // Read the comment on
-                                // HTMLCanvasElement::fetch_all_data.
-                                chan.send(None).unwrap();
-                            }
-                        }
-                    }
-                    CanvasMsg::FromLayout(message) => {
-                        match message {
-                            FromLayoutMsg::SendData(chan) =>
-                                painter.send_data(chan),
-                        }
-                    }
-                    CanvasMsg::Canvas2d(_) => panic!("Wrong message sent to WebGLThread"),
-                    CanvasMsg::WebVR(message) => painter.handle_webvr_message(message)
-                }
-            }
-        }).expect("Thread spawning failed");
-
-        result_port.recv().unwrap().map(|limits| (sender, limits))
-    }
-
-    fn send_data(&mut self, chan: IpcSender<CanvasData>) {
-        match self.data {
-            WebGLPaintTaskData::Readback {
-                ref context,
-                ref webrender_api,
-                ref mut image_key,
-                ref mut old_image_key,
-                ref mut very_old_image_key,
-            } => {
-                let width = self.size.width as usize;
-                let height = self.size.height as usize;
-
-                let mut pixels = context.gl().read_pixels(0, 0,
-                                                          self.size.width as gl::GLsizei,
-                                                          self.size.height as gl::GLsizei,
-                                                          gl::RGBA, gl::UNSIGNED_BYTE);
-                // flip image vertically (texture is upside down)
-                let orig_pixels = pixels.clone();
-                let stride = width * 4;
-                for y in 0..height {
-                    let dst_start = y * stride;
-                    let src_start = (height - y - 1) * stride;
-                    let src_slice = &orig_pixels[src_start .. src_start + stride];
-                    (&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
-                }
-
-                // rgba -> bgra
-                byte_swap(&mut pixels);
-
-                let descriptor = webrender_api::ImageDescriptor {
-                    width: width as u32,
-                    height: height as u32,
-                    stride: None,
-                    format: webrender_api::ImageFormat::BGRA8,
-                    offset: 0,
-                    is_opaque: false,
-                };
-                let data = webrender_api::ImageData::Raw(Arc::new(pixels));
-
-                let mut updates = webrender_api::ResourceUpdates::new();
-
-                match *image_key {
-                    Some(image_key) => {
-                        updates.update_image(image_key,
-                                             descriptor,
-                                             data,
-                                             None);
-                    }
-                    None => {
-                        *image_key = Some(webrender_api.generate_image_key());
-                        updates.add_image(image_key.unwrap(),
-                                          descriptor,
-                                          data,
-                                          None);
-                    }
-                }
-
-                if let Some(image_key) = mem::replace(very_old_image_key, old_image_key.take()) {
-                    updates.delete_image(image_key);
-                }
-
-                webrender_api.update_resources(updates);
-
-                let image_data = CanvasImageData {
-                    image_key: image_key.unwrap(),
-                };
-
-                chan.send(CanvasData::Image(image_data)).unwrap();
-            }
-            WebGLPaintTaskData::WebRender(_, id) => {
-                chan.send(CanvasData::WebGL(id)).unwrap();
-            }
-        }
-    }
-
-    #[allow(unsafe_code)]
-    fn recreate(&mut self, size: Size2D<i32>) -> Result<(), &'static str> {
-        match self.data {
-            WebGLPaintTaskData::Readback { ref mut context, ref mut image_key, ref mut old_image_key, .. }  => {
-                if size.width > self.size.width ||
-                   size.height > self.size.height {
-                    self.size = context.resize(size)?;
-                } else {
-                    self.size = size;
-                    context.gl().scissor(0, 0, size.width, size.height);
-                }
-                // Webrender doesn't let images change size, so we clear the webrender image key.
-                if let Some(image_key) = image_key.take() {
-                    // If this executes, then we are in a new epoch since we last recreated the canvas,
-                    // so `old_image_key` must be `None`.
-                    debug_assert!(old_image_key.is_none());
-                    *old_image_key = Some(image_key);
-                }
-            }
-            WebGLPaintTaskData::WebRender(ref api, id) => {
-                let device_size = webrender_api::DeviceIntSize::from_untyped(&size);
-                api.resize_webgl_context(id, &device_size);
-            }
-        }
-
-        Ok(())
-    }
-
-    fn init(&mut self) {
-        if let WebGLPaintTaskData::Readback { ref context, .. } = self.data {
-            context.make_current();
-        }
-    }
-}
-
-impl Drop for WebGLPaintThread {
-    fn drop(&mut self) {
-        if let WebGLPaintTaskData::Readback {
-            ref mut webrender_api,
-            image_key,
-            old_image_key,
-            very_old_image_key,
-            ..
-        } = self.data {
-            let mut updates = webrender_api::ResourceUpdates::new();
-
-            if let Some(image_key) = image_key {
-                updates.delete_image(image_key);
-            }
-            if let Some(image_key) = old_image_key {
-                updates.delete_image(image_key);
-            }
-            if let Some(image_key) = very_old_image_key {
-                updates.delete_image(image_key);
-            }
-
-            webrender_api.update_resources(updates);
-        }
-    }
-}
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas/webgl_thread.rs
@@ -0,0 +1,1204 @@
+/* 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::canvas::byte_swap;
+use canvas_traits::webgl::*;
+use euclid::Size2D;
+use gleam::gl;
+use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
+use std::collections::HashMap;
+use std::mem;
+use std::thread;
+use super::gl_context::{GLContextFactory, GLContextWrapper};
+use webrender;
+use webrender_api;
+
+/// WebGL Threading API entry point that lives in the constellation.
+/// It allows to get a WebGLThread handle for each script pipeline.
+pub use ::webgl_mode::WebGLThreads;
+
+/// A WebGLThread manages the life cycle and message multiplexing of
+/// a set of WebGLContexts living in the same thread.
+pub struct WebGLThread<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> {
+    /// Factory used to create a new GLContext shared with the WR/Main thread.
+    gl_factory: GLContextFactory,
+    /// Channel used to generate/update or delete `webrender_api::ImageKey`s.
+    webrender_api: webrender_api::RenderApi,
+    /// Map of live WebGLContexts.
+    contexts: HashMap<WebGLContextId, GLContextWrapper>,
+    /// Cached information for WebGLContexts.
+    cached_context_info: HashMap<WebGLContextId, WebGLContextInfo>,
+    /// Current bound context.
+    bound_context_id: Option<WebGLContextId>,
+    /// Id generator for new WebGLContexts.
+    next_webgl_id: usize,
+    /// Handler user to send WebVR commands.
+    webvr_compositor: Option<VR>,
+    /// Generic observer that listens WebGLContext creation, resize or removal events.
+    observer: OB,
+}
+
+impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR, OB> {
+    pub fn new(gl_factory: GLContextFactory,
+               webrender_api_sender: webrender_api::RenderApiSender,
+               webvr_compositor: Option<VR>,
+               observer: OB) -> Self {
+        WebGLThread {
+            gl_factory,
+            webrender_api: webrender_api_sender.create_api(),
+            contexts: HashMap::new(),
+            cached_context_info: HashMap::new(),
+            bound_context_id: None,
+            next_webgl_id: 0,
+            webvr_compositor,
+            observer: observer,
+        }
+    }
+
+    /// Creates a new `WebGLThread` and returns a Sender to
+    /// communicate with it.
+    pub fn start(gl_factory: GLContextFactory,
+                 webrender_api_sender: webrender_api::RenderApiSender,
+                 webvr_compositor: Option<VR>,
+                 observer: OB)
+                 -> WebGLSender<WebGLMsg> {
+        let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
+        let result = sender.clone();
+        thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || {
+            let mut renderer = WebGLThread::new(gl_factory,
+                                                webrender_api_sender,
+                                                webvr_compositor,
+                                                observer);
+            let webgl_chan = WebGLChan(sender);
+            loop {
+                let msg = receiver.recv().unwrap();
+                let exit = renderer.handle_msg(msg, &webgl_chan);
+                if exit {
+                    return;
+                }
+            }
+        }).expect("Thread spawning failed");
+
+        result
+    }
+
+    /// Handles a generic WebGLMsg message
+    #[inline]
+    fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool {
+        match msg {
+            WebGLMsg::CreateContext(size, attributes, result_sender) => {
+                let result = self.create_webgl_context(size, attributes);
+                result_sender.send(result.map(|(id, limits, share_mode)|
+                    WebGLCreateContextResult {
+                        sender: WebGLMsgSender::new(id, webgl_chan.clone()),
+                        limits: limits,
+                        share_mode: share_mode,
+                    }
+                )).unwrap();
+            },
+            WebGLMsg::ResizeContext(ctx_id, size, sender) => {
+                self.resize_webgl_context(ctx_id, size, sender);
+            },
+            WebGLMsg::RemoveContext(ctx_id) => {
+                self.remove_webgl_context(ctx_id);
+            },
+            WebGLMsg::WebGLCommand(ctx_id, command) => {
+                self.handle_webgl_command(ctx_id, command);
+            },
+            WebGLMsg::WebVRCommand(ctx_id, command) => {
+                self.handle_webvr_command(ctx_id, command);
+            },
+            WebGLMsg::Lock(ctx_id, sender) => {
+                self.handle_lock(ctx_id, sender);
+            },
+            WebGLMsg::Unlock(ctx_id) => {
+                self.handle_unlock(ctx_id);
+            },
+            WebGLMsg::UpdateWebRenderImage(ctx_id, sender) => {
+                self.handle_update_wr_image(ctx_id, sender);
+            },
+            WebGLMsg::Exit => {
+                return true;
+            }
+        }
+
+        false
+    }
+
+    /// Handles a WebGLCommand for a specific WebGLContext
+    fn handle_webgl_command(&mut self, context_id: WebGLContextId, command: WebGLCommand) {
+        if let Some(ctx) = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id) {
+            ctx.apply_command(command);
+        }
+    }
+
+    /// Handles a WebVRCommand for a specific WebGLContext
+    fn handle_webvr_command(&mut self, context_id: WebGLContextId, command: WebVRCommand) {
+        Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id);
+        let texture = match command {
+            WebVRCommand::SubmitFrame(..) => {
+                self.cached_context_info.get(&context_id)
+            },
+            _ => None
+        };
+        self.webvr_compositor.as_mut().unwrap().handle(command, texture.map(|t| (t.texture_id, t.size)));
+    }
+
+    /// Handles a lock external callback received from webrender::ExternalImageHandler
+    fn handle_lock(&mut self, context_id: WebGLContextId, sender: WebGLSender<(u32, Size2D<i32>)>) {
+        let ctx = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id)
+                        .expect("WebGLContext not found in a WebGLMsg::Lock message");
+        let info = self.cached_context_info.get_mut(&context_id).unwrap();
+        // Use a OpenGL Fence to perform the lock.
+        info.gl_sync = Some(ctx.gl().fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0));
+
+        sender.send((info.texture_id, info.size)).unwrap();
+    }
+
+    /// Handles an unlock external callback received from webrender::ExternalImageHandler
+    fn handle_unlock(&mut self, context_id: WebGLContextId) {
+        let ctx = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id)
+                        .expect("WebGLContext not found in a WebGLMsg::Unlock message");
+        let info = self.cached_context_info.get_mut(&context_id).unwrap();
+        if let Some(gl_sync) = info.gl_sync.take() {
+            // glFlush must be called before glWaitSync.
+            ctx.gl().flush();
+            // Wait until the GLSync object is signaled.
+            ctx.gl().wait_sync(gl_sync, 0, gl::TIMEOUT_IGNORED);
+            // Release the GLSync object.
+            ctx.gl().delete_sync(gl_sync);
+        }
+    }
+
+    /// Creates a new WebGLContext
+    fn create_webgl_context(&mut self,
+                            size: Size2D<i32>,
+                            attributes: GLContextAttributes)
+                            -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode), String> {
+        // First try to create a shared context for the best performance.
+        // Fallback to readback mode if the shared context creation fails.
+        let result = self.gl_factory.new_shared_context(size, attributes)
+                                    .map(|r| (r, WebGLContextShareMode::SharedTexture))
+                                    .or_else(|_| {
+                                        let ctx = self.gl_factory.new_context(size, attributes);
+                                        ctx.map(|r| (r, WebGLContextShareMode::Readback))
+                                    });
+
+        // Creating a new GLContext may make the current bound context_id dirty.
+        // Clear it to ensure that  make_current() is called in subsequent commands.
+        self.bound_context_id = None;
+
+        match result {
+            Ok((ctx, share_mode)) => {
+                let id = WebGLContextId(self.next_webgl_id);
+                let (size, texture_id, limits) = ctx.get_info();
+                self.next_webgl_id += 1;
+                self.contexts.insert(id, ctx);
+                self.cached_context_info.insert(id, WebGLContextInfo {
+                    texture_id,
+                    size,
+                    alpha: attributes.alpha,
+                    image_key: None,
+                    share_mode,
+                    gl_sync: None,
+                    old_image_key: None,
+                    very_old_image_key: None,
+                });
+
+                self.observer.on_context_create(id, texture_id, size);
+
+                Ok((id, limits, share_mode))
+            },
+            Err(msg) => {
+                Err(msg.to_owned())
+            }
+        }
+    }
+
+    /// Resizes a WebGLContext
+    fn resize_webgl_context(&mut self,
+                            context_id: WebGLContextId,
+                            size: Size2D<i32>,
+                            sender: WebGLSender<Result<(), String>>) {
+        let ctx = Self::make_current_if_needed_mut(context_id, &mut self.contexts, &mut self.bound_context_id);
+        match ctx.resize(size) {
+            Ok(_) => {
+                let (real_size, texture_id, _) = ctx.get_info();
+                self.observer.on_context_resize(context_id, texture_id, real_size);
+
+                let info = self.cached_context_info.get_mut(&context_id).unwrap();
+                // Update webgl texture size. Texture id may change too.
+                info.texture_id = texture_id;
+                info.size = real_size;
+                // WR doesn't support resizing and requires to create a new `ImageKey`.
+                // Mark the current image_key to be deleted later in the next epoch.
+                if let Some(image_key) = info.image_key.take() {
+                    // If this executes, then we are in a new epoch since we last recreated the canvas,
+                    // so `old_image_key` must be `None`.
+                    debug_assert!(info.old_image_key.is_none());
+                    info.old_image_key = Some(image_key);
+                }
+
+                sender.send(Ok(())).unwrap();
+            },
+            Err(msg) => {
+                sender.send(Err(msg.into())).unwrap();
+            }
+        }
+    }
+
+    /// Removes a WebGLContext and releases attached resources.
+    fn remove_webgl_context(&mut self, context_id: WebGLContextId) {
+        // Release webrender image keys.
+        if let Some(info) = self.cached_context_info.remove(&context_id) {
+            let mut updates = webrender_api::ResourceUpdates::new();
+
+            if let Some(image_key) = info.image_key {
+                updates.delete_image(image_key);
+            }
+            if let Some(image_key) = info.old_image_key {
+                updates.delete_image(image_key);
+            }
+            if let Some(image_key) = info.very_old_image_key {
+                updates.delete_image(image_key);
+            }
+
+            self.webrender_api.update_resources(updates)
+        }
+
+        // Release GL context.
+        if self.contexts.remove(&context_id).is_some() {
+            self.observer.on_context_delete(context_id);
+        }
+
+        // Removing a GLContext may make the current bound context_id dirty.
+        self.bound_context_id = None;
+    }
+
+    /// Handles the creation/update of webrender_api::ImageKeys fpr a specific WebGLContext.
+    /// This method is invoked from a UpdateWebRenderImage message sent by the layout thread.
+    /// If SharedTexture is used the UpdateWebRenderImage message is sent only after a WebGLContext creation or resize.
+    /// If Readback is used UpdateWebRenderImage message is sent always on each layout iteration in order to
+    /// submit the updated raw pixels.
+    fn handle_update_wr_image(&mut self, context_id: WebGLContextId, sender: WebGLSender<webrender_api::ImageKey>) {
+        let info = self.cached_context_info.get_mut(&context_id).unwrap();
+        let webrender_api = &self.webrender_api;
+
+        let image_key = match info.share_mode {
+            WebGLContextShareMode::SharedTexture => {
+                let size = info.size;
+                let alpha = info.alpha;
+                // Reuse existing ImageKey or generate a new one.
+                // When using a shared texture ImageKeys are only generated after a WebGLContext creation or resize.
+                *info.image_key.get_or_insert_with(|| {
+                    Self::create_wr_external_image(webrender_api, size, alpha, context_id)
+                })
+            },
+            WebGLContextShareMode::Readback => {
+                let pixels = Self::raw_pixels(&self.contexts[&context_id], info.size);
+                match info.image_key.clone() {
+                    Some(image_key) => {
+                        // ImageKey was already created, but WR Images must
+                        // be updated every frame in readback mode to send the new raw pixels.
+                        Self::update_wr_readback_image(webrender_api,
+                                                       info.size,
+                                                       info.alpha,
+                                                       image_key,
+                                                       pixels);
+
+                        image_key
+                    },
+                    None => {
+                        // Generate a new ImageKey for Readback mode.
+                        let image_key = Self::create_wr_readback_image(webrender_api,
+                                                                       info.size,
+                                                                       info.alpha,
+                                                                       pixels);
+                        info.image_key = Some(image_key);
+                        image_key
+                    }
+                }
+            }
+        };
+
+        // Delete old image
+        if let Some(image_key) = mem::replace(&mut info.very_old_image_key, info.old_image_key.take()) {
+            let mut updates = webrender_api::ResourceUpdates::new();
+            updates.delete_image(image_key);
+            self.webrender_api.update_resources(updates);
+        }
+
+        // Send the ImageKey to the Layout thread.
+        sender.send(image_key).unwrap();
+    }
+
+    /// Gets a reference to a GLContextWrapper for a given WebGLContextId and makes it current if required.
+    fn make_current_if_needed<'a>(context_id: WebGLContextId,
+                                  contexts: &'a HashMap<WebGLContextId, GLContextWrapper>,
+                                  bound_id: &mut Option<WebGLContextId>) -> Option<&'a GLContextWrapper> {
+        contexts.get(&context_id).and_then(|ctx| {
+            if Some(context_id) != *bound_id {
+                ctx.make_current();
+                *bound_id = Some(context_id);
+            }
+
+            Some(ctx)
+        })
+    }
+
+    /// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required.
+    fn make_current_if_needed_mut<'a>(context_id: WebGLContextId,
+                                      contexts: &'a mut HashMap<WebGLContextId, GLContextWrapper>,
+                                      bound_id: &mut Option<WebGLContextId>) -> &'a mut GLContextWrapper {
+        let ctx = contexts.get_mut(&context_id).expect("WebGLContext not found!");
+        if Some(context_id) != *bound_id {
+            ctx.make_current();
+            *bound_id = Some(context_id);
+        }
+        ctx
+    }
+
+    /// Creates a `webrender_api::ImageKey` that uses shared textures.
+    fn create_wr_external_image(webrender_api: &webrender_api::RenderApi,
+                                size: Size2D<i32>,
+                                alpha: bool,
+                                context_id: WebGLContextId) -> webrender_api::ImageKey {
+        let descriptor = Self::image_descriptor(size, alpha);
+
+        let data = webrender_api::ExternalImageData {
+            id: webrender_api::ExternalImageId(context_id.0 as u64),
+            channel_index: 0,
+            image_type: webrender_api::ExternalImageType::Texture2DHandle,
+        };
+        let data = webrender_api::ImageData::External(data);
+
+        let image_key = webrender_api.generate_image_key();
+        let mut updates = webrender_api::ResourceUpdates::new();
+        updates.add_image(image_key,
+                          descriptor,
+                          data,
+                          None);
+        webrender_api.update_resources(updates);
+
+        image_key
+    }
+
+    /// Creates a `webrender_api::ImageKey` that uses raw pixels.
+    fn create_wr_readback_image(webrender_api: &webrender_api::RenderApi,
+                                size: Size2D<i32>,
+                                alpha: bool,
+                                data: Vec<u8>) -> webrender_api::ImageKey {
+        let descriptor = Self::image_descriptor(size, alpha);
+        let data = webrender_api::ImageData::new(data);
+
+        let image_key = webrender_api.generate_image_key();
+        let mut updates = webrender_api::ResourceUpdates::new();
+        updates.add_image(image_key,
+                          descriptor,
+                          data,
+                          None);
+        webrender_api.update_resources(updates);
+
+        image_key
+    }
+
+    /// Updates a `webrender_api::ImageKey` that uses raw pixels.
+    fn update_wr_readback_image(webrender_api: &webrender_api::RenderApi,
+                                size: Size2D<i32>,
+                                alpha: bool,
+                                image_key: webrender_api::ImageKey,
+                                data: Vec<u8>) {
+        let descriptor = Self::image_descriptor(size, alpha);
+        let data = webrender_api::ImageData::new(data);
+
+        let mut updates = webrender_api::ResourceUpdates::new();
+        updates.update_image(image_key,
+                             descriptor,
+                             data,
+                             None);
+        webrender_api.update_resources(updates);
+    }
+
+    /// Helper function to create a `webrender_api::ImageDescriptor`.
+    fn image_descriptor(size: Size2D<i32>, alpha: bool) -> webrender_api::ImageDescriptor {
+        webrender_api::ImageDescriptor {
+            width: size.width as u32,
+            height: size.height as u32,
+            stride: None,
+            format: if alpha { webrender_api::ImageFormat::BGRA8 } else { webrender_api::ImageFormat::RGB8 },
+            offset: 0,
+            is_opaque: !alpha,
+        }
+    }
+
+    /// Helper function to fetch the raw pixels used in readback mode.
+    fn raw_pixels(context: &GLContextWrapper, size: Size2D<i32>) -> Vec<u8> {
+        let width = size.width as usize;
+        let height = size.height as usize;
+
+        let mut pixels = context.gl().read_pixels(0, 0,
+                                                  size.width as gl::GLsizei,
+                                                  size.height as gl::GLsizei,
+                                                  gl::RGBA, gl::UNSIGNED_BYTE);
+        // flip image vertically (texture is upside down)
+        let orig_pixels = pixels.clone();
+        let stride = width * 4;
+        for y in 0..height {
+            let dst_start = y * stride;
+            let src_start = (height - y - 1) * stride;
+            let src_slice = &orig_pixels[src_start .. src_start + stride];
+            (&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
+        }
+        byte_swap(&mut pixels);
+        pixels
+    }
+}
+
+impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> Drop for WebGLThread<VR, OB> {
+    fn drop(&mut self) {
+        // Call remove_context functions in order to correctly delete WebRender image keys.
+        let context_ids: Vec<WebGLContextId> = self.contexts.keys().map(|id| *id).collect();
+        for id in context_ids {
+            self.remove_webgl_context(id);
+        }
+    }
+}
+
+/// Helper struct to store cached WebGLContext information.
+struct WebGLContextInfo {
+    /// Render to texture identifier used by the WebGLContext.
+    texture_id: u32,
+    /// Size of the WebGLContext.
+    size: Size2D<i32>,
+    /// True if the WebGLContext uses an alpha channel.
+    alpha: bool,
+    /// Currently used WebRender image key.
+    image_key: Option<webrender_api::ImageKey>,
+    /// The sharing mode used to send the image to WebRender.
+    share_mode: WebGLContextShareMode,
+    /// GLSync Object used for a correct synchronization with Webrender external image callbacks.
+    gl_sync: Option<gl::GLsync>,
+    /// An old WebRender image key that can be deleted when the next epoch ends.
+    old_image_key: Option<webrender_api::ImageKey>,
+    /// An old WebRender image key that can be deleted when the current epoch ends.
+    very_old_image_key: Option<webrender_api::ImageKey>,
+}
+
+/// Trait used to observe events in a WebGL Thread.
+/// Used in webrender::ExternalImageHandler when multiple WebGL threads are used.
+pub trait WebGLThreadObserver: Send + 'static {
+    fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>);
+    fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>);
+    fn on_context_delete(&mut self, ctx_id: WebGLContextId);
+}
+
+/// This trait is used as a bridge between the `WebGLThreads` implementation and
+/// the WR ExternalImageHandler API implemented in the `WebGLExternalImageHandler` struct.
+/// `WebGLExternalImageHandler<T>` takes care of type conversions between WR and WebGL info (e.g keys, uvs).
+/// It uses this trait to notify lock/unlock messages and get the required info that WR needs.
+/// `WebGLThreads` receives lock/unlock message notifications and takes care of sending
+/// the unlock/lock messages to the appropiate `WebGLThread`.
+pub trait WebGLExternalImageApi {
+    fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>);
+    fn unlock(&mut self, ctx_id: WebGLContextId);
+}
+
+/// WebRender External Image Handler implementation
+pub struct WebGLExternalImageHandler<T: WebGLExternalImageApi> {
+    handler: T,
+}
+
+impl<T: WebGLExternalImageApi> WebGLExternalImageHandler<T> {
+    pub fn new(handler: T) -> Self {
+        Self {
+            handler: handler
+        }
+    }
+}
+
+impl<T: WebGLExternalImageApi> webrender::ExternalImageHandler for WebGLExternalImageHandler<T> {
+    /// Lock the external image. Then, WR could start to read the image content.
+    /// The WR client should not change the image content until the unlock() call.
+    fn lock(&mut self,
+            key: webrender_api::ExternalImageId,
+            _channel_index: u8) -> webrender::ExternalImage {
+        let ctx_id = WebGLContextId(key.0 as _);
+        let (texture_id, size) = self.handler.lock(ctx_id);
+
+        webrender::ExternalImage {
+            u0: 0.0,
+            u1: size.width as f32,
+            v1: 0.0,
+            v0: size.height as f32,
+            source: webrender::ExternalImageSource::NativeTexture(texture_id),
+        }
+
+    }
+    /// Unlock the external image. The WR should not read the image content
+    /// after this call.
+    fn unlock(&mut self,
+              key: webrender_api::ExternalImageId,
+              _channel_index: u8) {
+        let ctx_id = WebGLContextId(key.0 as _);
+        self.handler.unlock(ctx_id);
+    }
+}
+
+/// WebGL Commands Implementation
+pub struct WebGLImpl;
+
+impl WebGLImpl {
+    pub fn apply<Native: NativeGLContextMethods>(ctx: &GLContext<Native>, command: WebGLCommand) {
+        match command {
+            WebGLCommand::GetContextAttributes(sender) =>
+                sender.send(*ctx.borrow_attributes()).unwrap(),
+            WebGLCommand::ActiveTexture(target) =>
+                ctx.gl().active_texture(target),
+            WebGLCommand::AttachShader(program_id, shader_id) =>
+                ctx.gl().attach_shader(program_id.get(), shader_id.get()),
+            WebGLCommand::DetachShader(program_id, shader_id) =>
+                ctx.gl().detach_shader(program_id.get(), shader_id.get()),
+            WebGLCommand::BindAttribLocation(program_id, index, name) =>
+                ctx.gl().bind_attrib_location(program_id.get(), index, &name),
+            WebGLCommand::BlendColor(r, g, b, a) =>
+                ctx.gl().blend_color(r, g, b, a),
+            WebGLCommand::BlendEquation(mode) =>
+                ctx.gl().blend_equation(mode),
+            WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) =>
+                ctx.gl().blend_equation_separate(mode_rgb, mode_alpha),
+            WebGLCommand::BlendFunc(src, dest) =>
+                ctx.gl().blend_func(src, dest),
+            WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) =>
+                ctx.gl().blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha),
+            WebGLCommand::BufferData(buffer_type, data, usage) =>
+                gl::buffer_data(ctx.gl(), buffer_type, &data, usage),
+            WebGLCommand::BufferSubData(buffer_type, offset, data) =>
+                gl::buffer_sub_data(ctx.gl(), buffer_type, offset, &data),
+            WebGLCommand::Clear(mask) =>
+                ctx.gl().clear(mask),
+            WebGLCommand::ClearColor(r, g, b, a) =>
+                ctx.gl().clear_color(r, g, b, a),
+            WebGLCommand::ClearDepth(depth) =>
+                ctx.gl().clear_depth(depth),
+            WebGLCommand::ClearStencil(stencil) =>
+                ctx.gl().clear_stencil(stencil),
+            WebGLCommand::ColorMask(r, g, b, a) =>
+                ctx.gl().color_mask(r, g, b, a),
+            WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, width, height, border) =>
+                ctx.gl().copy_tex_image_2d(target, level, internal_format, x, y, width, height, border),
+            WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) =>
+                ctx.gl().copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height),
+            WebGLCommand::CullFace(mode) =>
+                ctx.gl().cull_face(mode),
+            WebGLCommand::DepthFunc(func) =>
+                ctx.gl().depth_func(func),
+            WebGLCommand::DepthMask(flag) =>
+                ctx.gl().depth_mask(flag),
+            WebGLCommand::DepthRange(near, far) =>
+                ctx.gl().depth_range(near, far),
+            WebGLCommand::Disable(cap) =>
+                ctx.gl().disable(cap),
+            WebGLCommand::Enable(cap) =>
+                ctx.gl().enable(cap),
+            WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) =>
+                ctx.gl().framebuffer_renderbuffer(target, attachment, renderbuffertarget,
+                                                  rb.map_or(0, WebGLRenderbufferId::get)),
+            WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) =>
+                ctx.gl().framebuffer_texture_2d(target, attachment, textarget,
+                                                texture.map_or(0, WebGLTextureId::get), level),
+            WebGLCommand::FrontFace(mode) =>
+                ctx.gl().front_face(mode),
+            WebGLCommand::DisableVertexAttribArray(attrib_id) =>
+                ctx.gl().disable_vertex_attrib_array(attrib_id),
+            WebGLCommand::DrawArrays(mode, first, count) =>
+                ctx.gl().draw_arrays(mode, first, count),
+            WebGLCommand::DrawElements(mode, count, type_, offset) =>
+                ctx.gl().draw_elements(mode, count, type_, offset as u32),
+            WebGLCommand::EnableVertexAttribArray(attrib_id) =>
+                ctx.gl().enable_vertex_attrib_array(attrib_id),
+            WebGLCommand::Hint(name, val) =>
+                ctx.gl().hint(name, val),
+            WebGLCommand::IsEnabled(cap, chan) =>
+                chan.send(ctx.gl().is_enabled(cap) != 0).unwrap(),
+            WebGLCommand::LineWidth(width) =>
+                ctx.gl().line_width(width),
+            WebGLCommand::PixelStorei(name, val) =>
+                ctx.gl().pixel_store_i(name, val),
+            WebGLCommand::PolygonOffset(factor, units) =>
+                ctx.gl().polygon_offset(factor, units),
+            WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, chan) =>
+                Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan),
+            WebGLCommand::RenderbufferStorage(target, format, width, height) =>
+                ctx.gl().renderbuffer_storage(target, format, width, height),
+            WebGLCommand::SampleCoverage(value, invert) =>
+                ctx.gl().sample_coverage(value, invert),
+            WebGLCommand::Scissor(x, y, width, height) =>
+                ctx.gl().scissor(x, y, width, height),
+            WebGLCommand::StencilFunc(func, ref_, mask) =>
+                ctx.gl().stencil_func(func, ref_, mask),
+            WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) =>
+                ctx.gl().stencil_func_separate(face, func, ref_, mask),
+            WebGLCommand::StencilMask(mask) =>
+                ctx.gl().stencil_mask(mask),
+            WebGLCommand::StencilMaskSeparate(face, mask) =>
+                ctx.gl().stencil_mask_separate(face, mask),
+            WebGLCommand::StencilOp(fail, zfail, zpass) =>
+                ctx.gl().stencil_op(fail, zfail, zpass),
+            WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) =>
+                ctx.gl().stencil_op_separate(face, fail, zfail, zpass),
+            WebGLCommand::GetActiveAttrib(program_id, index, chan) =>
+                Self::active_attrib(ctx.gl(), program_id, index, chan),
+            WebGLCommand::GetActiveUniform(program_id, index, chan) =>
+                Self::active_uniform(ctx.gl(), program_id, index, chan),
+            WebGLCommand::GetAttribLocation(program_id, name, chan) =>
+                Self::attrib_location(ctx.gl(), program_id, name, chan),
+            WebGLCommand::GetVertexAttrib(index, pname, chan) =>
+                Self::vertex_attrib(ctx.gl(), index, pname, chan),
+            WebGLCommand::GetVertexAttribOffset(index, pname, chan) =>
+                Self::vertex_attrib_offset(ctx.gl(), index, pname, chan),
+            WebGLCommand::GetBufferParameter(target, param_id, chan) =>
+                Self::buffer_parameter(ctx.gl(), target, param_id, chan),
+            WebGLCommand::GetParameter(param_id, chan) =>
+                Self::parameter(ctx.gl(), param_id, chan),
+            WebGLCommand::GetProgramParameter(program_id, param_id, chan) =>
+                Self::program_parameter(ctx.gl(), program_id, param_id, chan),
+            WebGLCommand::GetShaderParameter(shader_id, param_id, chan) =>
+                Self::shader_parameter(ctx.gl(), shader_id, param_id, chan),
+            WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, chan) =>
+                Self::shader_precision_format(ctx.gl(), shader_type, precision_type, chan),
+            WebGLCommand::GetExtensions(chan) =>
+                Self::get_extensions(ctx.gl(), chan),
+            WebGLCommand::GetUniformLocation(program_id, name, chan) =>
+                Self::uniform_location(ctx.gl(), program_id, name, chan),
+            WebGLCommand::GetShaderInfoLog(shader_id, chan) =>
+                Self::shader_info_log(ctx.gl(), shader_id, chan),
+            WebGLCommand::GetProgramInfoLog(program_id, chan) =>
+                Self::program_info_log(ctx.gl(), program_id, chan),
+            WebGLCommand::CompileShader(shader_id, source) =>
+                Self::compile_shader(ctx.gl(), shader_id, source),
+            WebGLCommand::CreateBuffer(chan) =>
+                Self::create_buffer(ctx.gl(), chan),
+            WebGLCommand::CreateFramebuffer(chan) =>
+                Self::create_framebuffer(ctx.gl(), chan),
+            WebGLCommand::CreateRenderbuffer(chan) =>
+                Self::create_renderbuffer(ctx.gl(), chan),
+            WebGLCommand::CreateTexture(chan) =>
+                Self::create_texture(ctx.gl(), chan),
+            WebGLCommand::CreateProgram(chan) =>
+                Self::create_program(ctx.gl(), chan),
+            WebGLCommand::CreateShader(shader_type, chan) =>
+                Self::create_shader(ctx.gl(), shader_type, chan),
+            WebGLCommand::DeleteBuffer(id) =>
+                ctx.gl().delete_buffers(&[id.get()]),
+            WebGLCommand::DeleteFramebuffer(id) =>
+                ctx.gl().delete_framebuffers(&[id.get()]),
+            WebGLCommand::DeleteRenderbuffer(id) =>
+                ctx.gl().delete_renderbuffers(&[id.get()]),
+            WebGLCommand::DeleteTexture(id) =>
+                ctx.gl().delete_textures(&[id.get()]),
+            WebGLCommand::DeleteProgram(id) =>
+                ctx.gl().delete_program(id.get()),
+            WebGLCommand::DeleteShader(id) =>
+                ctx.gl().delete_shader(id.get()),
+            WebGLCommand::BindBuffer(target, id) =>
+                ctx.gl().bind_buffer(target, id.map_or(0, WebGLBufferId::get)),
+            WebGLCommand::BindFramebuffer(target, request) =>
+                Self::bind_framebuffer(ctx.gl(), target, request, ctx),
+            WebGLCommand::BindRenderbuffer(target, id) =>
+                ctx.gl().bind_renderbuffer(target, id.map_or(0, WebGLRenderbufferId::get)),
+            WebGLCommand::BindTexture(target, id) =>
+                ctx.gl().bind_texture(target, id.map_or(0, WebGLTextureId::get)),
+            WebGLCommand::LinkProgram(program_id) =>
+                ctx.gl().link_program(program_id.get()),
+            WebGLCommand::Uniform1f(uniform_id, v) =>
+                ctx.gl().uniform_1f(uniform_id, v),
+            WebGLCommand::Uniform1fv(uniform_id, v) =>
+                ctx.gl().uniform_1fv(uniform_id, &v),
+            WebGLCommand::Uniform1i(uniform_id, v) =>
+                ctx.gl().uniform_1i(uniform_id, v),
+            WebGLCommand::Uniform1iv(uniform_id, v) =>
+                ctx.gl().uniform_1iv(uniform_id, &v),
+            WebGLCommand::Uniform2f(uniform_id, x, y) =>
+                ctx.gl().uniform_2f(uniform_id, x, y),
+            WebGLCommand::Uniform2fv(uniform_id, v) =>
+                ctx.gl().uniform_2fv(uniform_id, &v),
+            WebGLCommand::Uniform2i(uniform_id, x, y) =>
+                ctx.gl().uniform_2i(uniform_id, x, y),
+            WebGLCommand::Uniform2iv(uniform_id, v) =>
+                ctx.gl().uniform_2iv(uniform_id, &v),
+            WebGLCommand::Uniform3f(uniform_id, x, y, z) =>
+                ctx.gl().uniform_3f(uniform_id, x, y, z),
+            WebGLCommand::Uniform3fv(uniform_id, v) =>
+                ctx.gl().uniform_3fv(uniform_id, &v),
+            WebGLCommand::Uniform3i(uniform_id, x, y, z) =>
+                ctx.gl().uniform_3i(uniform_id, x, y, z),
+            WebGLCommand::Uniform3iv(uniform_id, v) =>
+                ctx.gl().uniform_3iv(uniform_id, &v),
+            WebGLCommand::Uniform4f(uniform_id, x, y, z, w) =>
+                ctx.gl().uniform_4f(uniform_id, x, y, z, w),
+            WebGLCommand::Uniform4fv(uniform_id, v) =>
+                ctx.gl().uniform_4fv(uniform_id, &v),
+            WebGLCommand::Uniform4i(uniform_id, x, y, z, w) =>
+                ctx.gl().uniform_4i(uniform_id, x, y, z, w),
+            WebGLCommand::Uniform4iv(uniform_id, v) =>
+                ctx.gl().uniform_4iv(uniform_id, &v),
+            WebGLCommand::UniformMatrix2fv(uniform_id, transpose,  v) =>
+                ctx.gl().uniform_matrix_2fv(uniform_id, transpose, &v),
+            WebGLCommand::UniformMatrix3fv(uniform_id, transpose,  v) =>
+                ctx.gl().uniform_matrix_3fv(uniform_id, transpose, &v),
+            WebGLCommand::UniformMatrix4fv(uniform_id, transpose,  v) =>
+                ctx.gl().uniform_matrix_4fv(uniform_id, transpose, &v),
+            WebGLCommand::UseProgram(program_id) =>
+                ctx.gl().use_program(program_id.get()),
+            WebGLCommand::ValidateProgram(program_id) =>
+                ctx.gl().validate_program(program_id.get()),
+            WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) =>
+                ctx.gl().vertex_attrib_4f(attrib_id, x, y, z, w),
+            WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) =>
+                ctx.gl().vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset),
+            WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset) =>
+                ctx.gl().vertex_attrib_pointer(attrib_id, size, data_type, normalized, stride, offset),
+            WebGLCommand::Viewport(x, y, width, height) =>
+                ctx.gl().viewport(x, y, width, height),
+            WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, data) =>
+                ctx.gl().tex_image_2d(target, level, internal, width, height,
+                                      /*border*/0, format, data_type, Some(&data)),
+            WebGLCommand::TexParameteri(target, name, value) =>
+                ctx.gl().tex_parameter_i(target, name, value),
+            WebGLCommand::TexParameterf(target, name, value) =>
+                ctx.gl().tex_parameter_f(target, name, value),
+            WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, data) =>
+                ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, &data),
+            WebGLCommand::DrawingBufferWidth(sender) =>
+                sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(),
+            WebGLCommand::DrawingBufferHeight(sender) =>
+                sender.send(ctx.borrow_draw_buffer().unwrap().size().height).unwrap(),
+            WebGLCommand::Finish(sender) =>
+                Self::finish(ctx.gl(), sender),
+            WebGLCommand::Flush =>
+                ctx.gl().flush(),
+            WebGLCommand::GenerateMipmap(target) =>
+                ctx.gl().generate_mipmap(target),
+            WebGLCommand::CreateVertexArray(chan) =>
+                Self::create_vertex_array(ctx.gl(), chan),
+            WebGLCommand::DeleteVertexArray(id) =>
+                ctx.gl().delete_vertex_arrays(&[id.get()]),
+            WebGLCommand::BindVertexArray(id) =>
+                ctx.gl().bind_vertex_array(id.map_or(0, WebGLVertexArrayId::get)),
+        }
+
+        // TODO: update test expectations in order to enable debug assertions
+        //if cfg!(debug_assertions) {
+            let error = ctx.gl().get_error();
+            assert!(error == gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error);
+        //}
+    }
+
+    fn read_pixels(gl: &gl::Gl, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32,
+                   chan: WebGLSender<Vec<u8>>) {
+      let result = gl.read_pixels(x, y, width, height, format, pixel_type);
+      chan.send(result).unwrap()
+    }
+
+    fn active_attrib(gl: &gl::Gl,
+                     program_id: WebGLProgramId,
+                     index: u32,
+                     chan: WebGLSender<WebGLResult<(i32, u32, String)>>) {
+        let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_ATTRIBUTES) as u32 {
+            Err(WebGLError::InvalidValue)
+        } else {
+            Ok(gl.get_active_attrib(program_id.get(), index))
+        };
+        chan.send(result).unwrap();
+    }
+
+    fn active_uniform(gl: &gl::Gl,
+                      program_id: WebGLProgramId,
+                      index: u32,
+                      chan: WebGLSender<WebGLResult<(i32, u32, String)>>) {
+        let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_UNIFORMS) as u32 {
+            Err(WebGLError::InvalidValue)
+        } else {
+            Ok(gl.get_active_uniform(program_id.get(), index))
+        };
+        chan.send(result).unwrap();
+    }
+
+    fn attrib_location(gl: &gl::Gl,
+                       program_id: WebGLProgramId,
+                       name: String,
+                       chan: WebGLSender<Option<i32>> ) {
+        let attrib_location = gl.get_attrib_location(program_id.get(), &name);
+
+        let attrib_location = if attrib_location == -1 {
+            None
+        } else {
+            Some(attrib_location)
+        };
+
+        chan.send(attrib_location).unwrap();
+    }
+
+    fn parameter(gl: &gl::Gl,
+                 param_id: u32,
+                 chan: WebGLSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::ACTIVE_TEXTURE |
+            gl::ALPHA_BITS |
+            gl::BLEND_DST_ALPHA |
+            gl::BLEND_DST_RGB |
+            gl::BLEND_EQUATION_ALPHA |
+            gl::BLEND_EQUATION_RGB |
+            gl::BLEND_SRC_ALPHA |
+            gl::BLEND_SRC_RGB |
+            gl::BLUE_BITS |
+            gl::CULL_FACE_MODE |
+            gl::DEPTH_BITS |
+            gl::DEPTH_FUNC |
+            gl::FRONT_FACE |
+            //gl::GENERATE_MIPMAP_HINT |
+            gl::GREEN_BITS |
+            //gl::IMPLEMENTATION_COLOR_READ_FORMAT |
+            //gl::IMPLEMENTATION_COLOR_READ_TYPE |
+            gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS |
+            gl::MAX_CUBE_MAP_TEXTURE_SIZE |
+            //gl::MAX_FRAGMENT_UNIFORM_VECTORS |
+            gl::MAX_RENDERBUFFER_SIZE |
+            gl::MAX_TEXTURE_IMAGE_UNITS |
+            gl::MAX_TEXTURE_SIZE |
+            //gl::MAX_VARYING_VECTORS |
+            gl::MAX_VERTEX_ATTRIBS |
+            gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS |
+            //gl::MAX_VERTEX_UNIFORM_VECTORS |
+            gl::PACK_ALIGNMENT |
+            gl::RED_BITS |
+            gl::SAMPLE_BUFFERS |
+            gl::SAMPLES |
+            gl::STENCIL_BACK_FAIL |
+            gl::STENCIL_BACK_FUNC |
+            gl::STENCIL_BACK_PASS_DEPTH_FAIL |
+            gl::STENCIL_BACK_PASS_DEPTH_PASS |
+            gl::STENCIL_BACK_REF |
+            gl::STENCIL_BACK_VALUE_MASK |
+            gl::STENCIL_BACK_WRITEMASK |
+            gl::STENCIL_BITS |
+            gl::STENCIL_CLEAR_VALUE |
+            gl::STENCIL_FAIL |
+            gl::STENCIL_FUNC |
+            gl::STENCIL_PASS_DEPTH_FAIL |
+            gl::STENCIL_PASS_DEPTH_PASS |
+            gl::STENCIL_REF |
+            gl::STENCIL_VALUE_MASK |
+            gl::STENCIL_WRITEMASK |
+            gl::SUBPIXEL_BITS |
+            gl::UNPACK_ALIGNMENT =>
+            //gl::UNPACK_COLORSPACE_CONVERSION_WEBGL =>
+                Ok(WebGLParameter::Int(gl.get_integer_v(param_id))),
+
+            gl::BLEND |
+            gl::CULL_FACE |
+            gl::DEPTH_TEST |
+            gl::DEPTH_WRITEMASK |
+            gl::DITHER |
+            gl::POLYGON_OFFSET_FILL |
+            gl::SAMPLE_COVERAGE_INVERT |
+            gl::STENCIL_TEST =>
+            //gl::UNPACK_FLIP_Y_WEBGL |
+            //gl::UNPACK_PREMULTIPLY_ALPHA_WEBGL =>
+                Ok(WebGLParameter::Bool(gl.get_boolean_v(param_id) != 0)),
+
+            gl::DEPTH_CLEAR_VALUE |
+            gl::LINE_WIDTH |
+            gl::POLYGON_OFFSET_FACTOR |
+            gl::POLYGON_OFFSET_UNITS |
+            gl::SAMPLE_COVERAGE_VALUE =>
+                Ok(WebGLParameter::Float(gl.get_float_v(param_id))),
+
+            gl::VERSION => Ok(WebGLParameter::String("WebGL 1.0".to_owned())),
+            gl::RENDERER |
+            gl::VENDOR => Ok(WebGLParameter::String("Mozilla/Servo".to_owned())),
+            gl::SHADING_LANGUAGE_VERSION => Ok(WebGLParameter::String("WebGL GLSL ES 1.0".to_owned())),
+
+            // TODO(zbarsky, emilio): Implement support for the following valid parameters
+            // Float32Array
+            gl::ALIASED_LINE_WIDTH_RANGE |
+            //gl::ALIASED_POINT_SIZE_RANGE |
+            //gl::BLEND_COLOR |
+            gl::COLOR_CLEAR_VALUE |
+            gl::DEPTH_RANGE |
+
+            // WebGLBuffer
+            gl::ARRAY_BUFFER_BINDING |
+            gl::ELEMENT_ARRAY_BUFFER_BINDING |
+
+            // WebGLFrameBuffer
+            gl::FRAMEBUFFER_BINDING |
+
+            // WebGLRenderBuffer
+            gl::RENDERBUFFER_BINDING |
+
+            // WebGLProgram
+            gl::CURRENT_PROGRAM |
+
+            // WebGLTexture
+            gl::TEXTURE_BINDING_2D |
+            gl::TEXTURE_BINDING_CUBE_MAP |
+
+            // sequence<GlBoolean>
+            gl::COLOR_WRITEMASK |
+
+            // Uint32Array
+            gl::COMPRESSED_TEXTURE_FORMATS |
+
+            // Int32Array
+            gl::MAX_VIEWPORT_DIMS |
+            gl::SCISSOR_BOX |
+            gl::VIEWPORT => Err(WebGLError::InvalidEnum),
+
+            // Invalid parameters
+            _ => Err(WebGLError::InvalidEnum)
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn finish(gl: &gl::Gl, chan: WebGLSender<()>) {
+        gl.finish();
+        chan.send(()).unwrap();
+    }
+
+    fn vertex_attrib(gl: &gl::Gl,
+                     index: u32,
+                     pname: u32,
+                     chan: WebGLSender<WebGLResult<WebGLParameter>>) {
+        let result = if index >= gl.get_integer_v(gl::MAX_VERTEX_ATTRIBS) as u32 {
+            Err(WebGLError::InvalidValue)
+        } else {
+            match pname {
+                gl::VERTEX_ATTRIB_ARRAY_ENABLED |
+                gl::VERTEX_ATTRIB_ARRAY_NORMALIZED =>
+                    Ok(WebGLParameter::Bool(gl.get_vertex_attrib_iv(index, pname) != 0)),
+                gl::VERTEX_ATTRIB_ARRAY_SIZE |
+                gl::VERTEX_ATTRIB_ARRAY_STRIDE |
+                gl::VERTEX_ATTRIB_ARRAY_TYPE =>
+                    Ok(WebGLParameter::Int(gl.get_vertex_attrib_iv(index, pname))),
+                gl::CURRENT_VERTEX_ATTRIB =>
+                    Ok(WebGLParameter::FloatArray(gl.get_vertex_attrib_fv(index, pname))),
+                // gl::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING should return WebGLBuffer
+                _ => Err(WebGLError::InvalidEnum),
+            }
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn vertex_attrib_offset(gl: &gl::Gl,
+                            index: u32,
+                            pname: u32,
+                            chan: WebGLSender<WebGLResult<isize>>) {
+        let result = match pname {
+                gl::VERTEX_ATTRIB_ARRAY_POINTER => Ok(gl.get_vertex_attrib_pointer_v(index, pname)),
+                _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn buffer_parameter(gl: &gl::Gl,
+                        target: u32,
+                        param_id: u32,
+                        chan: WebGLSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::BUFFER_SIZE |
+            gl::BUFFER_USAGE =>
+                Ok(WebGLParameter::Int(gl.get_buffer_parameter_iv(target, param_id))),
+            _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn program_parameter(gl: &gl::Gl,
+                         program_id: WebGLProgramId,
+                         param_id: u32,
+                         chan: WebGLSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::DELETE_STATUS |
+            gl::LINK_STATUS |
+            gl::VALIDATE_STATUS =>
+                Ok(WebGLParameter::Bool(gl.get_program_iv(program_id.get(), param_id) != 0)),
+            gl::ATTACHED_SHADERS |
+            gl::ACTIVE_ATTRIBUTES |
+            gl::ACTIVE_UNIFORMS =>
+                Ok(WebGLParameter::Int(gl.get_program_iv(program_id.get(), param_id))),
+            _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn shader_parameter(gl: &gl::Gl,
+                        shader_id: WebGLShaderId,
+                        param_id: u32,
+                        chan: WebGLSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::SHADER_TYPE =>
+                Ok(WebGLParameter::Int(gl.get_shader_iv(shader_id.get(), param_id))),
+            gl::DELETE_STATUS |
+            gl::COMPILE_STATUS =>
+                Ok(WebGLParameter::Bool(gl.get_shader_iv(shader_id.get(), param_id) != 0)),
+            _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn shader_precision_format(gl: &gl::Gl,
+                               shader_type: u32,
+                               precision_type: u32,
+                               chan: WebGLSender<WebGLResult<(i32, i32, i32)>>) {
+        let result = match precision_type {
+            gl::LOW_FLOAT |
+            gl::MEDIUM_FLOAT |
+            gl::HIGH_FLOAT |
+            gl::LOW_INT |
+            gl::MEDIUM_INT |
+            gl::HIGH_INT => {
+                Ok(gl.get_shader_precision_format(shader_type, precision_type))
+            },
+            _=> {
+                Err(WebGLError::InvalidEnum)
+            }
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn get_extensions(gl: &gl::Gl, chan: WebGLSender<String>) {
+        chan.send(gl.get_string(gl::EXTENSIONS)).unwrap();
+    }
+
+    fn uniform_location(gl: &gl::Gl,
+                        program_id: WebGLProgramId,
+                        name: String,
+                        chan: WebGLSender<Option<i32>>) {
+        let location = gl.get_uniform_location(program_id.get(), &name);
+        let location = if location == -1 {
+            None
+        } else {
+            Some(location)
+        };
+
+        chan.send(location).unwrap();
+    }
+
+
+    fn shader_info_log(gl: &gl::Gl, shader_id: WebGLShaderId, chan: WebGLSender<String>) {
+        let log = gl.get_shader_info_log(shader_id.get());
+        chan.send(log).unwrap();
+    }
+
+    fn program_info_log(gl: &gl::Gl, program_id: WebGLProgramId, chan: WebGLSender<String>) {
+        let log = gl.get_program_info_log(program_id.get());
+        chan.send(log).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_buffer(gl: &gl::Gl, chan: WebGLSender<Option<WebGLBufferId>>) {
+        let buffer = gl.gen_buffers(1)[0];
+        let buffer = if buffer == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLBufferId::new(buffer) })
+        };
+        chan.send(buffer).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_framebuffer(gl: &gl::Gl, chan: WebGLSender<Option<WebGLFramebufferId>>) {
+        let framebuffer = gl.gen_framebuffers(1)[0];
+        let framebuffer = if framebuffer == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLFramebufferId::new(framebuffer) })
+        };
+        chan.send(framebuffer).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_renderbuffer(gl: &gl::Gl, chan: WebGLSender<Option<WebGLRenderbufferId>>) {
+        let renderbuffer = gl.gen_renderbuffers(1)[0];
+        let renderbuffer = if renderbuffer == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLRenderbufferId::new(renderbuffer) })
+        };
+        chan.send(renderbuffer).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_texture(gl: &gl::Gl, chan: WebGLSender<Option<WebGLTextureId>>) {
+        let texture = gl.gen_textures(1)[0];
+        let texture = if texture == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLTextureId::new(texture) })
+        };
+        chan.send(texture).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_program(gl: &gl::Gl, chan: WebGLSender<Option<WebGLProgramId>>) {
+        let program = gl.create_program();
+        let program = if program == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLProgramId::new(program) })
+        };
+        chan.send(program).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_shader(gl: &gl::Gl, shader_type: u32, chan: WebGLSender<Option<WebGLShaderId>>) {
+        let shader = gl.create_shader(shader_type);
+        let shader = if shader == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLShaderId::new(shader) })
+        };
+        chan.send(shader).unwrap();
+    }
+
+    #[allow(unsafe_code)]
+    fn create_vertex_array(gl: &gl::Gl, chan: WebGLSender<Option<WebGLVertexArrayId>>) {
+        let vao = gl.gen_vertex_arrays(1)[0];
+        let vao = if vao == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLVertexArrayId::new(vao) })
+        };
+        chan.send(vao).unwrap();
+    }
+
+    #[inline]
+    fn bind_framebuffer<Native: NativeGLContextMethods>(gl: &gl::Gl,
+                                                        target: u32,
+                                                        request: WebGLFramebufferBindingRequest,
+                                                        ctx: &GLContext<Native>) {
+        let id = match request {
+            WebGLFramebufferBindingRequest::Explicit(id) => id.get(),
+            WebGLFramebufferBindingRequest::Default =>
+                ctx.borrow_draw_buffer().unwrap().get_framebuffer(),
+        };
+
+        gl.bind_framebuffer(target, id);
+    }
+
+
+    #[inline]
+    fn compile_shader(gl: &gl::Gl, shader_id: WebGLShaderId, source: String) {
+        gl.shader_source(shader_id.get(), &[source.as_bytes()]);
+        gl.compile_shader(shader_id.get());
+    }
+}
--- a/servo/components/canvas_traits/Cargo.toml
+++ b/servo/components/canvas_traits/Cargo.toml
@@ -10,10 +10,13 @@ name = "canvas_traits"
 path = "lib.rs"
 
 [dependencies]
 cssparser = "0.19"
 euclid = "0.15"
 heapsize = "0.4"
 heapsize_derive = "0.1"
 ipc-channel = "0.8"
+lazy_static = "0.2"
+offscreen_gl_context = { version = "0.11", features = ["serde"] }
 serde = "1.0"
+servo_config = {path = "../config"}
 webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
copy from servo/components/canvas_traits/lib.rs
copy to servo/components/canvas_traits/canvas.rs
--- a/servo/components/canvas_traits/lib.rs
+++ b/servo/components/canvas_traits/canvas.rs
@@ -1,76 +1,40 @@
 /* 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/. */
 
-#![crate_name = "canvas_traits"]
-#![crate_type = "rlib"]
-
-#![deny(unsafe_code)]
-
-extern crate cssparser;
-extern crate euclid;
-extern crate heapsize;
-#[macro_use] extern crate heapsize_derive;
-extern crate ipc_channel;
-#[macro_use] extern crate serde;
-extern crate webrender_api;
-
 use cssparser::RGBA;
 use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D};
 use ipc_channel::ipc::IpcSender;
 use std::default::Default;
 use std::str::FromStr;
-use webrender_api::{WebGLCommand, WebGLContextId, VRCompositorCommand};
+use webrender_api;
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum FillRule {
     Nonzero,
     Evenodd,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum CanvasMsg {
     Canvas2d(Canvas2dMsg),
-    Common(CanvasCommonMsg),
     FromLayout(FromLayoutMsg),
     FromScript(FromScriptMsg),
-    WebGL(WebGLCommand),
-    WebVR(VRCompositorCommand)
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum CanvasCommonMsg {
+    Recreate(Size2D<i32>),
     Close,
-    Recreate(Size2D<i32>),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum CanvasData {
-    Image(CanvasImageData),
-    WebGL(WebGLContextId),
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub struct CanvasImageData {
     pub image_key: webrender_api::ImageKey,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
-pub enum FromLayoutMsg {
-    SendData(IpcSender<CanvasData>),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum FromScriptMsg {
-    SendPixels(IpcSender<Option<Vec<u8>>>),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
 pub enum Canvas2dMsg {
     Arc(Point2D<f32>, f32, f32, f32, bool),
     ArcTo(Point2D<f32>, Point2D<f32>, f32),
     DrawImage(Vec<u8>, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
     DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool),
     DrawImageInOther(
         IpcSender<CanvasMsg>, Size2D<f64>, Rect<f64>, Rect<f64>, bool, IpcSender<()>),
     BeginPath,
@@ -101,16 +65,26 @@ pub enum Canvas2dMsg {
     SetGlobalComposition(CompositionOrBlending),
     SetTransform(Transform2D<f32>),
     SetShadowOffsetX(f64),
     SetShadowOffsetY(f64),
     SetShadowBlur(f64),
     SetShadowColor(RGBA),
 }
 
+#[derive(Clone, Deserialize, Serialize)]
+pub enum FromLayoutMsg {
+    SendData(IpcSender<CanvasImageData>),
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum FromScriptMsg {
+    SendPixels(IpcSender<Option<Vec<u8>>>),
+}
+
 #[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
 pub struct CanvasGradientStop {
     pub offset: f64,
     pub color: RGBA,
 }
 
 #[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
 pub struct LinearGradientStyle {
--- a/servo/components/canvas_traits/lib.rs
+++ b/servo/components/canvas_traits/lib.rs
@@ -1,435 +1,25 @@
 /* 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/. */
 
 #![crate_name = "canvas_traits"]
 #![crate_type = "rlib"]
+#![feature(nonzero)]
 
 #![deny(unsafe_code)]
 
+extern crate core;
 extern crate cssparser;
 extern crate euclid;
 extern crate heapsize;
 #[macro_use] extern crate heapsize_derive;
 extern crate ipc_channel;
+#[macro_use] extern crate lazy_static;
+extern crate offscreen_gl_context;
 #[macro_use] extern crate serde;
+extern crate servo_config;
 extern crate webrender_api;
 
-use cssparser::RGBA;
-use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D};
-use ipc_channel::ipc::IpcSender;
-use std::default::Default;
-use std::str::FromStr;
-use webrender_api::{WebGLCommand, WebGLContextId, VRCompositorCommand};
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum FillRule {
-    Nonzero,
-    Evenodd,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum CanvasMsg {
-    Canvas2d(Canvas2dMsg),
-    Common(CanvasCommonMsg),
-    FromLayout(FromLayoutMsg),
-    FromScript(FromScriptMsg),
-    WebGL(WebGLCommand),
-    WebVR(VRCompositorCommand)
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum CanvasCommonMsg {
-    Close,
-    Recreate(Size2D<i32>),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum CanvasData {
-    Image(CanvasImageData),
-    WebGL(WebGLContextId),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct CanvasImageData {
-    pub image_key: webrender_api::ImageKey,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum FromLayoutMsg {
-    SendData(IpcSender<CanvasData>),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum FromScriptMsg {
-    SendPixels(IpcSender<Option<Vec<u8>>>),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum Canvas2dMsg {
-    Arc(Point2D<f32>, f32, f32, f32, bool),
-    ArcTo(Point2D<f32>, Point2D<f32>, f32),
-    DrawImage(Vec<u8>, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
-    DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool),
-    DrawImageInOther(
-        IpcSender<CanvasMsg>, Size2D<f64>, Rect<f64>, Rect<f64>, bool, IpcSender<()>),
-    BeginPath,
-    BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>),
-    ClearRect(Rect<f32>),
-    Clip,
-    ClosePath,
-    Fill,
-    FillRect(Rect<f32>),
-    GetImageData(Rect<i32>, Size2D<f64>, IpcSender<Vec<u8>>),
-    IsPointInPath(f64, f64, FillRule, IpcSender<bool>),
-    LineTo(Point2D<f32>),
-    MoveTo(Point2D<f32>),
-    PutImageData(Vec<u8>, Vector2D<f64>, Size2D<f64>, Rect<f64>),
-    QuadraticCurveTo(Point2D<f32>, Point2D<f32>),
-    Rect(Rect<f32>),
-    RestoreContext,
-    SaveContext,
-    StrokeRect(Rect<f32>),
-    Stroke,
-    SetFillStyle(FillOrStrokeStyle),
-    SetStrokeStyle(FillOrStrokeStyle),
-    SetLineWidth(f32),
-    SetLineCap(LineCapStyle),
-    SetLineJoin(LineJoinStyle),
-    SetMiterLimit(f32),
-    SetGlobalAlpha(f32),
-    SetGlobalComposition(CompositionOrBlending),
-    SetTransform(Transform2D<f32>),
-    SetShadowOffsetX(f64),
-    SetShadowOffsetY(f64),
-    SetShadowBlur(f64),
-    SetShadowColor(RGBA),
-}
-
-#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
-pub struct CanvasGradientStop {
-    pub offset: f64,
-    pub color: RGBA,
-}
-
-#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
-pub struct LinearGradientStyle {
-    pub x0: f64,
-    pub y0: f64,
-    pub x1: f64,
-    pub y1: f64,
-    pub stops: Vec<CanvasGradientStop>
-}
-
-impl LinearGradientStyle {
-    pub fn new(x0: f64, y0: f64, x1: f64, y1: f64, stops: Vec<CanvasGradientStop>)
-        -> LinearGradientStyle {
-        LinearGradientStyle {
-            x0: x0,
-            y0: y0,
-            x1: x1,
-            y1: y1,
-            stops: stops,
-        }
-    }
-}
-
-#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
-pub struct RadialGradientStyle {
-    pub x0: f64,
-    pub y0: f64,
-    pub r0: f64,
-    pub x1: f64,
-    pub y1: f64,
-    pub r1: f64,
-    pub stops: Vec<CanvasGradientStop>
-}
-
-impl RadialGradientStyle {
-    pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64, stops: Vec<CanvasGradientStop>)
-        -> RadialGradientStyle {
-        RadialGradientStyle {
-            x0: x0,
-            y0: y0,
-            r0: r0,
-            x1: x1,
-            y1: y1,
-            r1: r1,
-            stops: stops,
-        }
-    }
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct SurfaceStyle {
-    pub surface_data: Vec<u8>,
-    pub surface_size: Size2D<i32>,
-    pub repeat_x: bool,
-    pub repeat_y: bool,
-}
-
-impl SurfaceStyle {
-    pub fn new(surface_data: Vec<u8>, surface_size: Size2D<i32>, repeat_x: bool, repeat_y: bool)
-        -> SurfaceStyle {
-        SurfaceStyle {
-            surface_data: surface_data,
-            surface_size: surface_size,
-            repeat_x: repeat_x,
-            repeat_y: repeat_y,
-        }
-    }
-}
-
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum FillOrStrokeStyle {
-    Color(RGBA),
-    LinearGradient(LinearGradientStyle),
-    RadialGradient(RadialGradientStyle),
-    Surface(SurfaceStyle),
-}
-
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, HeapSizeOf)]
-pub enum LineCapStyle {
-    Butt = 0,
-    Round = 1,
-    Square = 2,
-}
-
-impl FromStr for LineCapStyle {
-    type Err = ();
-
-    fn from_str(string: &str) -> Result<LineCapStyle, ()> {
-        match string {
-            "butt" => Ok(LineCapStyle::Butt),
-            "round" => Ok(LineCapStyle::Round),
-            "square" => Ok(LineCapStyle::Square),
-            _ => Err(()),
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, HeapSizeOf)]
-pub enum LineJoinStyle {
-    Round = 0,
-    Bevel = 1,
-    Miter = 2,
-}
-
-impl FromStr for LineJoinStyle {
-    type Err = ();
-
-    fn from_str(string: &str) -> Result<LineJoinStyle, ()> {
-        match string {
-            "round" => Ok(LineJoinStyle::Round),
-            "bevel" => Ok(LineJoinStyle::Bevel),
-            "miter" => Ok(LineJoinStyle::Miter),
-            _ => Err(()),
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize)]
-pub enum RepetitionStyle {
-    Repeat,
-    RepeatX,
-    RepeatY,
-    NoRepeat,
-}
-
-impl FromStr for RepetitionStyle {
-    type Err = ();
-
-    fn from_str(string: &str) -> Result<RepetitionStyle, ()> {
-        match string {
-            "repeat" => Ok(RepetitionStyle::Repeat),
-            "repeat-x" => Ok(RepetitionStyle::RepeatX),
-            "repeat-y" => Ok(RepetitionStyle::RepeatY),
-            "no-repeat" => Ok(RepetitionStyle::NoRepeat),
-            _ => Err(()),
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, HeapSizeOf)]
-pub enum CompositionStyle {
-    SrcIn,
-    SrcOut,
-    SrcOver,
-    SrcAtop,
-    DestIn,
-    DestOut,
-    DestOver,
-    DestAtop,
-    Copy,
-    Lighter,
-    Xor,
-}
-
-impl FromStr for CompositionStyle {
-    type Err = ();
-
-    fn from_str(string: &str) -> Result<CompositionStyle, ()> {
-        match string {
-            "source-in"        => Ok(CompositionStyle::SrcIn),
-            "source-out"       => Ok(CompositionStyle::SrcOut),
-            "source-over"      => Ok(CompositionStyle::SrcOver),
-            "source-atop"      => Ok(CompositionStyle::SrcAtop),
-            "destination-in"   => Ok(CompositionStyle::DestIn),
-            "destination-out"  => Ok(CompositionStyle::DestOut),
-            "destination-over" => Ok(CompositionStyle::DestOver),
-            "destination-atop" => Ok(CompositionStyle::DestAtop),
-            "copy"             => Ok(CompositionStyle::Copy),
-            "lighter"          => Ok(CompositionStyle::Lighter),
-            "xor"              => Ok(CompositionStyle::Xor),
-            _ => Err(())
-        }
-    }
-}
-
-impl CompositionStyle {
-    pub fn to_str(&self) -> &str {
-        match *self {
-            CompositionStyle::SrcIn    => "source-in",
-            CompositionStyle::SrcOut   => "source-out",
-            CompositionStyle::SrcOver  => "source-over",
-            CompositionStyle::SrcAtop  => "source-atop",
-            CompositionStyle::DestIn   => "destination-in",
-            CompositionStyle::DestOut  => "destination-out",
-            CompositionStyle::DestOver => "destination-over",
-            CompositionStyle::DestAtop => "destination-atop",
-            CompositionStyle::Copy     => "copy",
-            CompositionStyle::Lighter  => "lighter",
-            CompositionStyle::Xor      => "xor",
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, HeapSizeOf)]
-pub enum BlendingStyle {
-    Multiply,
-    Screen,
-    Overlay,
-    Darken,
-    Lighten,
-    ColorDodge,
-    ColorBurn,
-    HardLight,
-    SoftLight,
-    Difference,
-    Exclusion,
-    Hue,
-    Saturation,
-    Color,
-    Luminosity,
-}
-
-impl FromStr for BlendingStyle {
-    type Err = ();
-
-    fn from_str(string: &str) -> Result<BlendingStyle, ()> {
-        match string {
-            "multiply"    => Ok(BlendingStyle::Multiply),
-            "screen"      => Ok(BlendingStyle::Screen),
-            "overlay"     => Ok(BlendingStyle::Overlay),
-            "darken"      => Ok(BlendingStyle::Darken),
-            "lighten"     => Ok(BlendingStyle::Lighten),
-            "color-dodge" => Ok(BlendingStyle::ColorDodge),
-            "color-burn"  => Ok(BlendingStyle::ColorBurn),
-            "hard-light"  => Ok(BlendingStyle::HardLight),
-            "soft-light"  => Ok(BlendingStyle::SoftLight),
-            "difference"  => Ok(BlendingStyle::Difference),
-            "exclusion"   => Ok(BlendingStyle::Exclusion),
-            "hue"         => Ok(BlendingStyle::Hue),
-            "saturation"  => Ok(BlendingStyle::Saturation),
-            "color"       => Ok(BlendingStyle::Color),
-            "luminosity"  => Ok(BlendingStyle::Luminosity),
-            _ => Err(())
-        }
-    }
-}
-
-impl BlendingStyle {
-    pub fn to_str(&self) -> &str {
-        match *self {
-            BlendingStyle::Multiply   => "multiply",
-            BlendingStyle::Screen     => "screen",
-            BlendingStyle::Overlay    => "overlay",
-            BlendingStyle::Darken     => "darken",
-            BlendingStyle::Lighten    => "lighten",
-            BlendingStyle::ColorDodge => "color-dodge",
-            BlendingStyle::ColorBurn  => "color-burn",
-            BlendingStyle::HardLight  => "hard-light",
-            BlendingStyle::SoftLight  => "soft-light",
-            BlendingStyle::Difference => "difference",
-            BlendingStyle::Exclusion  => "exclusion",
-            BlendingStyle::Hue        => "hue",
-            BlendingStyle::Saturation => "saturation",
-            BlendingStyle::Color      => "color",
-            BlendingStyle::Luminosity => "luminosity",
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, HeapSizeOf)]
-pub enum CompositionOrBlending {
-    Composition(CompositionStyle),
-    Blending(BlendingStyle),
-}
-
-impl Default for CompositionOrBlending {
-    fn default() -> CompositionOrBlending {
-        CompositionOrBlending::Composition(CompositionStyle::SrcOver)
-    }
-}
-
-impl FromStr for CompositionOrBlending {
-    type Err = ();
-
-    fn from_str(string: &str) -> Result<CompositionOrBlending, ()> {
-        if let Ok(op) = CompositionStyle::from_str(string) {
-            return Ok(CompositionOrBlending::Composition(op));
-        }
-
-        if let Ok(op) = BlendingStyle::from_str(string) {
-            return Ok(CompositionOrBlending::Blending(op));
-        }
-
-        Err(())
-    }
-}
-
-// TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this.
-pub fn byte_swap(data: &mut [u8]) {
-    let length = data.len();
-    // FIXME(rust #27741): Range::step_by is not stable yet as of this writing.
-    let mut i = 0;
-    while i < length {
-        let r = data[i + 2];
-        data[i + 2] = data[i + 0];
-        data[i + 0] = r;
-        i += 4;
-    }
-}
-
-pub fn multiply_u8_pixel(a: u8, b: u8) -> u8 {
-    return (a as u32 * b as u32 / 255) as u8;
-}
-
-pub fn byte_swap_and_premultiply(data: &mut [u8]) {
-    let length = data.len();
-
-    let mut i = 0;
-    while i < length {
-        let r = data[i + 2];
-        let g = data[i + 1];
-        let b = data[i + 0];
-        let a = data[i + 3];
-
-        data[i + 0] = multiply_u8_pixel(r, a);
-        data[i + 1] = multiply_u8_pixel(g, a);
-        data[i + 2] = multiply_u8_pixel(b, a);
-
-        i += 4;
-    }
-}
+pub mod canvas;
+pub mod webgl;
+mod webgl_channel;
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas_traits/webgl.rs
@@ -0,0 +1,506 @@
+/* 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 core::nonzero::NonZero;
+use euclid::Size2D;
+use offscreen_gl_context::{GLContextAttributes, GLLimits};
+use std::fmt;
+use webrender_api;
+
+/// Sender type used in WebGLCommands.
+pub use ::webgl_channel::WebGLSender;
+/// Receiver type used in WebGLCommands.
+pub use ::webgl_channel::WebGLReceiver;
+/// Result type for send()/recv() calls in in WebGLCommands.
+pub use ::webgl_channel::WebGLSendResult;
+/// Helper function that creates a WebGL channel (WebGLSender, WebGLReceiver) to be used in WebGLCommands.
+pub use ::webgl_channel::webgl_channel;
+/// Entry point type used in a Script Pipeline to get the WebGLChan to be used in that thread.
+pub use ::webgl_channel::WebGLPipeline;
+/// Entry point channel type used for sending WebGLMsg messages to the WebGL renderer.
+pub use ::webgl_channel::WebGLChan;
+
+/// WebGL Message API
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebGLMsg {
+    /// Creates a new WebGLContext.
+    CreateContext(Size2D<i32>, GLContextAttributes, WebGLSender<Result<(WebGLCreateContextResult), String>>),
+    /// Resizes a WebGLContext.
+    ResizeContext(WebGLContextId, Size2D<i32>, WebGLSender<Result<(), String>>),
+    /// Drops a WebGLContext.
+    RemoveContext(WebGLContextId),
+    /// Runs a WebGLCommand in a specific WebGLContext.
+    WebGLCommand(WebGLContextId, WebGLCommand),
+    /// Runs a WebVRCommand in a specific WebGLContext.
+    WebVRCommand(WebGLContextId, WebVRCommand),
+    /// Locks a specific WebGLContext. Lock messages are used for a correct synchronization
+    /// with WebRender external image API.
+    /// WR locks a external texture when it wants to use the shared texture contents.
+    /// The WR client should not change the shared texture content until the Unlock call.
+    /// Currently OpenGL Sync Objects are used to implement the synchronization mechanism.
+    Lock(WebGLContextId, WebGLSender<(u32, Size2D<i32>)>),
+    /// Unlocks a specific WebGLContext. Unlock messages are used for a correct synchronization
+    /// with WebRender external image API.
+    /// The WR unlocks a context when it finished reading the shared texture contents.
+    /// Unlock messages are always sent after a Lock message.
+    Unlock(WebGLContextId),
+    /// Creates or updates the image keys required for WebRender.
+    UpdateWebRenderImage(WebGLContextId, WebGLSender<webrender_api::ImageKey>),
+    /// Frees all resources and closes the thread.
+    Exit,
+}
+
+/// Contains the WebGLCommand sender and information about a WebGLContext
+#[derive(Clone, Deserialize, Serialize)]
+pub struct WebGLCreateContextResult {
+    /// Sender instance to send commands to the specific WebGLContext
+    pub sender: WebGLMsgSender,
+    /// Information about the internal GL Context.
+    pub limits: GLLimits,
+    /// How the WebGLContext is shared with WebRender.
+    pub share_mode: WebGLContextShareMode,
+}
+
+#[derive(Clone, Copy, Deserialize, HeapSizeOf, Serialize)]
+pub enum WebGLContextShareMode {
+    /// Fast: a shared texture_id is used in WebRender.
+    SharedTexture,
+    /// Slow: glReadPixels is used to send pixels to WebRender each frame.
+    Readback,
+}
+
+/// Helper struct to send WebGLCommands to a specific WebGLContext.
+#[derive(Clone, Deserialize, HeapSizeOf, Serialize)]
+pub struct WebGLMsgSender {
+    ctx_id: WebGLContextId,
+    #[ignore_heap_size_of = "channels are hard"]
+    sender: WebGLChan,
+}
+
+impl WebGLMsgSender {
+    pub fn new(id: WebGLContextId, sender: WebGLChan) -> Self {
+        WebGLMsgSender {
+            ctx_id: id,
+            sender: sender,
+        }
+    }
+
+    /// Send a WebGLCommand message
+    #[inline]
+    pub fn send(&self, command: WebGLCommand) -> WebGLSendResult {
+        self.sender.send(WebGLMsg::WebGLCommand(self.ctx_id, command))
+    }
+
+    /// Send a WebVRCommand message
+    #[inline]
+    pub fn send_vr(&self, command: WebVRCommand) -> WebGLSendResult {
+        self.sender.send(WebGLMsg::WebVRCommand(self.ctx_id, command))
+    }
+
+    /// Send a resize message
+    #[inline]
+    pub fn send_resize(&self,
+                       size: Size2D<i32>,
+                       sender: WebGLSender<Result<(), String>>)
+                       -> WebGLSendResult {
+        self.sender.send(WebGLMsg::ResizeContext(self.ctx_id, size, sender))
+    }
+
+    #[inline]
+    pub fn send_remove(&self) -> WebGLSendResult {
+        self.sender.send(WebGLMsg::RemoveContext(self.ctx_id))
+    }
+
+    #[inline]
+    pub fn send_update_wr_image(&self, sender: WebGLSender<webrender_api::ImageKey>) -> WebGLSendResult {
+        self.sender.send(WebGLMsg::UpdateWebRenderImage(self.ctx_id, sender))
+    }
+}
+
+/// WebGL Commands for a specific WebGLContext
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebGLCommand {
+    GetContextAttributes(WebGLSender<GLContextAttributes>),
+    ActiveTexture(u32),
+    BlendColor(f32, f32, f32, f32),
+    BlendEquation(u32),
+    BlendEquationSeparate(u32, u32),
+    BlendFunc(u32, u32),
+    BlendFuncSeparate(u32, u32, u32, u32),
+    AttachShader(WebGLProgramId, WebGLShaderId),
+    DetachShader(WebGLProgramId, WebGLShaderId),
+    BindAttribLocation(WebGLProgramId, u32, String),
+    BufferData(u32, Vec<u8>, u32),
+    BufferSubData(u32, isize, Vec<u8>),
+    Clear(u32),
+    ClearColor(f32, f32, f32, f32),
+    ClearDepth(f64),
+    ClearStencil(i32),
+    ColorMask(bool, bool, bool, bool),
+    CullFace(u32),
+    FrontFace(u32),
+    DepthFunc(u32),
+    DepthMask(bool),
+    DepthRange(f64, f64),
+    Enable(u32),
+    Disable(u32),
+    CompileShader(WebGLShaderId, String),
+    CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32),
+    CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32),
+    CreateBuffer(WebGLSender<Option<WebGLBufferId>>),
+    CreateFramebuffer(WebGLSender<Option<WebGLFramebufferId>>),
+    CreateRenderbuffer(WebGLSender<Option<WebGLRenderbufferId>>),
+    CreateTexture(WebGLSender<Option<WebGLTextureId>>),
+    CreateProgram(WebGLSender<Option<WebGLProgramId>>),
+    CreateShader(u32, WebGLSender<Option<WebGLShaderId>>),
+    DeleteBuffer(WebGLBufferId),
+    DeleteFramebuffer(WebGLFramebufferId),
+    DeleteRenderbuffer(WebGLRenderbufferId),
+    DeleteTexture(WebGLTextureId),
+    DeleteProgram(WebGLProgramId),
+    DeleteShader(WebGLShaderId),
+    BindBuffer(u32, Option<WebGLBufferId>),
+    BindFramebuffer(u32, WebGLFramebufferBindingRequest),
+    BindRenderbuffer(u32, Option<WebGLRenderbufferId>),
+    BindTexture(u32, Option<WebGLTextureId>),
+    DisableVertexAttribArray(u32),
+    DrawArrays(u32, i32, i32),
+    DrawElements(u32, i32, u32, i64),
+    EnableVertexAttribArray(u32),
+    FramebufferRenderbuffer(u32, u32, u32, Option<WebGLRenderbufferId>),
+    FramebufferTexture2D(u32, u32, u32, Option<WebGLTextureId>, i32),
+    GetBufferParameter(u32, u32, WebGLSender<WebGLResult<WebGLParameter>>),
+    GetExtensions(WebGLSender<String>),
+    GetParameter(u32, WebGLSender<WebGLResult<WebGLParameter>>),
+    GetProgramParameter(WebGLProgramId, u32, WebGLSender<WebGLResult<WebGLParameter>>),
+    GetShaderParameter(WebGLShaderId, u32, WebGLSender<WebGLResult<WebGLParameter>>),
+    GetShaderPrecisionFormat(u32, u32, WebGLSender<WebGLResult<(i32, i32, i32)>>),
+    GetActiveAttrib(WebGLProgramId, u32, WebGLSender<WebGLResult<(i32, u32, String)>>),
+    GetActiveUniform(WebGLProgramId, u32, WebGLSender<WebGLResult<(i32, u32, String)>>),
+    GetAttribLocation(WebGLProgramId, String, WebGLSender<Option<i32>>),
+    GetUniformLocation(WebGLProgramId, String, WebGLSender<Option<i32>>),
+    GetVertexAttrib(u32, u32, WebGLSender<WebGLResult<WebGLParameter>>),
+    GetVertexAttribOffset(u32, u32, WebGLSender<WebGLResult<isize>>),
+    GetShaderInfoLog(WebGLShaderId, WebGLSender<String>),
+    GetProgramInfoLog(WebGLProgramId, WebGLSender<String>),
+    PolygonOffset(f32, f32),
+    RenderbufferStorage(u32, u32, i32, i32),
+    ReadPixels(i32, i32, i32, i32, u32, u32, WebGLSender<Vec<u8>>),
+    SampleCoverage(f32, bool),
+    Scissor(i32, i32, i32, i32),
+    StencilFunc(u32, i32, u32),
+    StencilFuncSeparate(u32, u32, i32, u32),
+    StencilMask(u32),
+    StencilMaskSeparate(u32, u32),
+    StencilOp(u32, u32, u32),
+    StencilOpSeparate(u32, u32, u32, u32),
+    Hint(u32, u32),
+    IsEnabled(u32, WebGLSender<bool>),
+    LineWidth(f32),
+    PixelStorei(u32, i32),
+    LinkProgram(WebGLProgramId),
+    Uniform1f(i32, f32),
+    Uniform1fv(i32, Vec<f32>),
+    Uniform1i(i32, i32),
+    Uniform1iv(i32, Vec<i32>),
+    Uniform2f(i32, f32, f32),
+    Uniform2fv(i32, Vec<f32>),
+    Uniform2i(i32, i32, i32),
+    Uniform2iv(i32, Vec<i32>),
+    Uniform3f(i32, f32, f32, f32),
+    Uniform3fv(i32, Vec<f32>),
+    Uniform3i(i32, i32, i32, i32),
+    Uniform3iv(i32, Vec<i32>),
+    Uniform4f(i32, f32, f32, f32, f32),
+    Uniform4fv(i32, Vec<f32>),
+    Uniform4i(i32, i32, i32, i32, i32),
+    Uniform4iv(i32, Vec<i32>),
+    UniformMatrix2fv(i32, bool, Vec<f32>),
+    UniformMatrix3fv(i32, bool, Vec<f32>),
+    UniformMatrix4fv(i32, bool, Vec<f32>),
+    UseProgram(WebGLProgramId),
+    ValidateProgram(WebGLProgramId),
+    VertexAttrib(u32, f32, f32, f32, f32),
+    VertexAttribPointer(u32, i32, u32, bool, i32, u32),
+    VertexAttribPointer2f(u32, i32, bool, i32, u32),
+    Viewport(i32, i32, i32, i32),
+    TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec<u8>),
+    TexParameteri(u32, u32, i32),
+    TexParameterf(u32, u32, f32),
+    TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, Vec<u8>),
+    DrawingBufferWidth(WebGLSender<i32>),
+    DrawingBufferHeight(WebGLSender<i32>),
+    Finish(WebGLSender<()>),
+    Flush,
+    GenerateMipmap(u32),
+    CreateVertexArray(WebGLSender<Option<WebGLVertexArrayId>>),
+    DeleteVertexArray(WebGLVertexArrayId),
+    BindVertexArray(Option<WebGLVertexArrayId>),
+}
+
+macro_rules! define_resource_id_struct {
+    ($name:ident) => {
+        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
+        pub struct $name(NonZero<u32>);
+
+        impl $name {
+            #[allow(unsafe_code)]
+            #[inline]
+            pub unsafe fn new(id: u32) -> Self {
+                $name(NonZero::new_unchecked(id))
+            }
+
+            #[inline]
+            pub fn get(self) -> u32 {
+                self.0.get()
+            }
+        }
+
+    };
+}
+
+macro_rules! define_resource_id {
+    ($name:ident) => {
+        define_resource_id_struct!($name);
+
+        #[allow(unsafe_code)]
+        impl<'de> ::serde::Deserialize<'de> for $name {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+                where D: ::serde::Deserializer<'de>
+            {
+                let id = try!(u32::deserialize(deserializer));
+                if id == 0 {
+                    Err(::serde::de::Error::custom("expected a non-zero value"))
+                } else {
+                    Ok(unsafe { $name::new(id) })
+                }
+            }
+        }
+
+        impl ::serde::Serialize for $name {
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                where S: ::serde::Serializer
+            {
+                self.get().serialize(serializer)
+            }
+        }
+
+        impl ::std::fmt::Debug for $name {
+            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
+                  -> Result<(), ::std::fmt::Error> {
+                fmt.debug_tuple(stringify!($name))
+                   .field(&self.get())
+                   .finish()
+            }
+        }
+
+        impl ::std::fmt::Display for $name {
+            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
+                  -> Result<(), ::std::fmt::Error> {
+                write!(fmt, "{}", self.get())
+            }
+        }
+
+        impl ::heapsize::HeapSizeOf for $name {
+            fn heap_size_of_children(&self) -> usize { 0 }
+        }
+    }
+}
+
+define_resource_id!(WebGLBufferId);
+define_resource_id!(WebGLFramebufferId);
+define_resource_id!(WebGLRenderbufferId);
+define_resource_id!(WebGLTextureId);
+define_resource_id!(WebGLProgramId);
+define_resource_id!(WebGLShaderId);
+define_resource_id!(WebGLVertexArrayId);
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct WebGLContextId(pub usize);
+
+impl ::heapsize::HeapSizeOf for WebGLContextId {
+    fn heap_size_of_children(&self) -> usize { 0 }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum WebGLError {
+    InvalidEnum,
+    InvalidFramebufferOperation,
+    InvalidOperation,
+    InvalidValue,
+    OutOfMemory,
+    ContextLost,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLFramebufferBindingRequest {
+    Explicit(WebGLFramebufferId),
+    Default,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLParameter {
+    Int(i32),
+    Bool(bool),
+    String(String),
+    Float(f32),
+    FloatArray(Vec<f32>),
+    Invalid,
+}
+
+pub type WebGLResult<T> = Result<T, WebGLError>;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLShaderParameter {
+    Int(i32),
+    Bool(bool),
+    Invalid,
+}
+
+pub type WebVRDeviceId = u32;
+
+// WebVR commands that must be called in the WebGL render thread.
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebVRCommand {
+    /// Start presenting to a VR device.
+    Create(WebVRDeviceId),
+    /// Synchronize the pose information to be used in the frame.
+    SyncPoses(WebVRDeviceId, f64, f64, WebGLSender<Result<Vec<u8>, ()>>),
+    /// Submit the frame to a VR device using the specified texture coordinates.
+    SubmitFrame(WebVRDeviceId, [f32; 4], [f32; 4]),
+    /// Stop presenting to a VR device
+    Release(WebVRDeviceId)
+}
+
+// Trait object that handles WebVR commands.
+// Receives the texture id and size associated to the WebGLContext.
+pub trait WebVRRenderHandler: Send {
+    fn handle(&mut self, command: WebVRCommand, texture: Option<(u32, Size2D<i32>)>);
+}
+
+impl fmt::Debug for WebGLCommand {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::WebGLCommand::*;
+        let name = match *self {
+            GetContextAttributes(..) => "GetContextAttributes",
+            ActiveTexture(..) => "ActiveTexture",
+            BlendColor(..) => "BlendColor",
+            BlendEquation(..) => "BlendEquation",
+            BlendEquationSeparate(..) => "BlendEquationSeparate",
+            BlendFunc(..) => "BlendFunc",
+            BlendFuncSeparate(..) => "BlendFuncSeparate",
+            AttachShader(..) => "AttachShader",
+            DetachShader(..) => "DetachShader",
+            BindAttribLocation(..) => "BindAttribLocation",
+            BufferData(..) => "BufferData",
+            BufferSubData(..) => "BufferSubData",
+            Clear(..) => "Clear",
+            ClearColor(..) => "ClearColor",
+            ClearDepth(..) => "ClearDepth",
+            ClearStencil(..) => "ClearStencil",
+            ColorMask(..) => "ColorMask",
+            CopyTexImage2D(..) => "CopyTexImage2D",
+            CopyTexSubImage2D(..) => "CopyTexSubImage2D",
+            CullFace(..) => "CullFace",
+            FrontFace(..) => "FrontFace",
+            DepthFunc(..) => "DepthFunc",
+            DepthMask(..) => "DepthMask",
+            DepthRange(..) => "DepthRange",
+            Enable(..) => "Enable",
+            Disable(..) => "Disable",
+            CompileShader(..) => "CompileShader",
+            CreateBuffer(..) => "CreateBuffer",
+            CreateFramebuffer(..) => "CreateFramebuffer",
+            CreateRenderbuffer(..) => "CreateRenderbuffer",
+            CreateTexture(..) => "CreateTexture",
+            CreateProgram(..) => "CreateProgram",
+            CreateShader(..) => "CreateShader",
+            DeleteBuffer(..) => "DeleteBuffer",
+            DeleteFramebuffer(..) => "DeleteFramebuffer",
+            DeleteRenderbuffer(..) => "DeleteRenderBuffer",
+            DeleteTexture(..) => "DeleteTexture",
+            DeleteProgram(..) => "DeleteProgram",
+            DeleteShader(..) => "DeleteShader",
+            BindBuffer(..) => "BindBuffer",
+            BindFramebuffer(..) => "BindFramebuffer",
+            BindRenderbuffer(..) => "BindRenderbuffer",
+            BindTexture(..) => "BindTexture",
+            DisableVertexAttribArray(..) => "DisableVertexAttribArray",
+            DrawArrays(..) => "DrawArrays",
+            DrawElements(..) => "DrawElements",
+            EnableVertexAttribArray(..) => "EnableVertexAttribArray",
+            FramebufferRenderbuffer(..) => "FramebufferRenderbuffer",
+            FramebufferTexture2D(..) => "FramebufferTexture2D",
+            GetBufferParameter(..) => "GetBufferParameter",
+            GetExtensions(..) => "GetExtensions",
+            GetParameter(..) => "GetParameter",
+            GetProgramParameter(..) => "GetProgramParameter",
+            GetShaderParameter(..) => "GetShaderParameter",
+            GetShaderPrecisionFormat(..) => "GetShaderPrecisionFormat",
+            GetActiveAttrib(..) => "GetActiveAttrib",
+            GetActiveUniform(..) => "GetActiveUniform",
+            GetAttribLocation(..) => "GetAttribLocation",
+            GetUniformLocation(..) => "GetUniformLocation",
+            GetShaderInfoLog(..) => "GetShaderInfoLog",
+            GetProgramInfoLog(..) => "GetProgramInfoLog",
+            GetVertexAttrib(..) => "GetVertexAttrib",
+            GetVertexAttribOffset(..) => "GetVertexAttribOffset",
+            PolygonOffset(..) => "PolygonOffset",
+            ReadPixels(..) => "ReadPixels",
+            RenderbufferStorage(..) => "RenderbufferStorage",
+            SampleCoverage(..) => "SampleCoverage",
+            Scissor(..) => "Scissor",
+            StencilFunc(..) => "StencilFunc",
+            StencilFuncSeparate(..) => "StencilFuncSeparate",
+            StencilMask(..) => "StencilMask",
+            StencilMaskSeparate(..) => "StencilMaskSeparate",
+            StencilOp(..) => "StencilOp",
+            StencilOpSeparate(..) => "StencilOpSeparate",
+            Hint(..) => "Hint",
+            IsEnabled(..) => "IsEnabled",
+            LineWidth(..) => "LineWidth",
+            PixelStorei(..) => "PixelStorei",
+            LinkProgram(..) => "LinkProgram",
+            Uniform1f(..) => "Uniform1f",
+            Uniform1fv(..) => "Uniform1fv",
+            Uniform1i(..) => "Uniform1i",
+            Uniform1iv(..) => "Uniform1iv",
+            Uniform2f(..) => "Uniform2f",
+            Uniform2fv(..) => "Uniform2fv",
+            Uniform2i(..) => "Uniform2i",
+            Uniform2iv(..) => "Uniform2iv",
+            Uniform3f(..) => "Uniform3f",
+            Uniform3fv(..) => "Uniform3fv",
+            Uniform3i(..) => "Uniform3i",
+            Uniform3iv(..) => "Uniform3iv",
+            Uniform4f(..) => "Uniform4f",
+            Uniform4fv(..) => "Uniform4fv",
+            Uniform4i(..) => "Uniform4i",
+            Uniform4iv(..) => "Uniform4iv",
+            UniformMatrix2fv(..) => "UniformMatrix2fv",
+            UniformMatrix3fv(..) => "UniformMatrix3fv",
+            UniformMatrix4fv(..) => "UniformMatrix4fv",
+            UseProgram(..) => "UseProgram",
+            ValidateProgram(..) => "ValidateProgram",
+            VertexAttrib(..) => "VertexAttrib",
+            VertexAttribPointer2f(..) => "VertexAttribPointer2f",
+            VertexAttribPointer(..) => "VertexAttribPointer",
+            Viewport(..) => "Viewport",
+            TexImage2D(..) => "TexImage2D",
+            TexParameteri(..) => "TexParameteri",
+            TexParameterf(..) => "TexParameterf",
+            TexSubImage2D(..) => "TexSubImage2D",
+            DrawingBufferWidth(..) => "DrawingBufferWidth",
+            DrawingBufferHeight(..) => "DrawingBufferHeight",
+            Finish(..) => "Finish",
+            Flush => "Flush",
+            GenerateMipmap(..) => "GenerateMipmap",
+            CreateVertexArray(..) => "CreateVertexArray",
+            DeleteVertexArray(..) => "DeleteVertexArray",
+            BindVertexArray(..) => "BindVertexArray"
+        };
+
+        write!(f, "CanvasWebGLMsg::{}(..)", name)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas_traits/webgl_channel/ipc.rs
@@ -0,0 +1,15 @@
+/* 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 ipc_channel;
+use serde::{Deserialize, Serialize};
+use std::io;
+
+pub type WebGLSender<T> = ipc_channel::ipc::IpcSender<T>;
+pub type WebGLReceiver<T> = ipc_channel::ipc::IpcReceiver<T>;
+
+pub fn webgl_channel<T: Serialize + for<'de> Deserialize<'de>>()
+        -> Result<(WebGLSender<T>, WebGLReceiver<T>), io::Error> {
+    ipc_channel::ipc::channel()
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas_traits/webgl_channel/mod.rs
@@ -0,0 +1,87 @@
+/* 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/. */
+
+//! Enum wrappers to be able to select different channel implementations at runtime.
+
+mod ipc;
+mod mpsc;
+
+use ::webgl::WebGLMsg;
+use serde::{Deserialize, Serialize};
+use servo_config::opts;
+
+lazy_static! {
+    static ref IS_MULTIPROCESS: bool = {
+        opts::multiprocess()
+    };
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebGLSender<T: Serialize> {
+    Ipc(ipc::WebGLSender<T>),
+    Mpsc(mpsc::WebGLSender<T>),
+}
+
+impl<T: Serialize> WebGLSender<T> {
+    #[inline]
+    pub fn send(&self, msg: T) -> WebGLSendResult {
+        match *self {
+            WebGLSender::Ipc(ref sender) => {
+                sender.send(msg).map_err(|_| ())
+            },
+            WebGLSender::Mpsc(ref sender) => {
+                sender.send(msg).map_err(|_| ())
+            }
+        }
+    }
+}
+
+pub type WebGLSendResult = Result<(), ()>;
+
+pub enum WebGLReceiver<T> where T: for<'de> Deserialize<'de> + Serialize {
+    Ipc(ipc::WebGLReceiver<T>),
+    Mpsc(mpsc::WebGLReceiver<T>),
+}
+
+impl<T> WebGLReceiver<T> where T: for<'de> Deserialize<'de> + Serialize {
+    pub fn recv(&self) -> Result<T, ()> {
+        match *self {
+            WebGLReceiver::Ipc(ref receiver) => {
+                receiver.recv().map_err(|_| ())
+            },
+            WebGLReceiver::Mpsc(ref receiver) => {
+                receiver.recv().map_err(|_| ())
+            }
+        }
+    }
+}
+
+pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()>
+                             where T: for<'de> Deserialize<'de> + Serialize {
+    if *IS_MULTIPROCESS {
+        ipc::webgl_channel().map(|(tx, rx)| (WebGLSender::Ipc(tx), WebGLReceiver::Ipc(rx)))
+                            .map_err(|_| ())
+    } else {
+        mpsc::webgl_channel().map(|(tx, rx)| (WebGLSender::Mpsc(tx), WebGLReceiver::Mpsc(rx)))
+    }
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct WebGLChan(pub WebGLSender<WebGLMsg>);
+
+impl WebGLChan {
+    #[inline]
+    pub fn send(&self, msg: WebGLMsg) -> WebGLSendResult {
+        self.0.send(msg)
+    }
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct WebGLPipeline(pub WebGLChan);
+
+impl WebGLPipeline {
+    pub fn channel(&self) -> WebGLChan {
+        self.0.clone()
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/canvas_traits/webgl_channel/mpsc.rs
@@ -0,0 +1,51 @@
+/* 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 serde::{Deserialize, Serialize};
+use serde::{Deserializer, Serializer};
+use std::sync::mpsc;
+
+#[macro_use]
+macro_rules! unreachable_serializable {
+    ($name:ident) => {
+        impl<T> Serialize for $name<T> {
+            fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
+                unreachable!();
+            }
+        }
+
+        impl<'a, T> Deserialize<'a> for $name<T> {
+            fn deserialize<D>(_: D) -> Result<$name<T>, D::Error>
+                            where D: Deserializer<'a> {
+                unreachable!();
+            }
+        }
+    };
+}
+
+#[derive(Clone)]
+pub struct WebGLSender<T>(mpsc::Sender<T>);
+pub struct WebGLReceiver<T>(mpsc::Receiver<T>);
+
+impl<T> WebGLSender<T> {
+    #[inline]
+    pub fn send(&self, data: T) -> Result<(), mpsc::SendError<T>> {
+        self.0.send(data)
+    }
+}
+
+impl<T> WebGLReceiver<T> {
+    #[inline]
+    pub fn recv(&self) -> Result<T, mpsc::RecvError> {
+        self.0.recv()
+    }
+}
+
+pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()> {
+    let (sender, receiver) = mpsc::channel();
+    Ok((WebGLSender(sender), WebGLReceiver(receiver)))
+}
+
+unreachable_serializable!(WebGLReceiver);
+unreachable_serializable!(WebGLSender);
--- a/servo/components/constellation/Cargo.toml
+++ b/servo/components/constellation/Cargo.toml
@@ -25,17 +25,16 @@ hyper = "0.10"
 ipc-channel = "0.8"
 itertools = "0.5"
 layout_traits = {path = "../layout_traits"}
 log = "0.3.5"
 metrics = {path = "../metrics"}
 msg = {path = "../msg"}
 net = {path = "../net"}
 net_traits = {path = "../net_traits"}
-offscreen_gl_context = { version = "0.11", features = ["serde"] }
 profile_traits = {path = "../profile_traits"}
 script_traits = {path = "../script_traits"}
 serde = "1.0"
 style_traits = {path = "../style_traits"}
 servo_config = {path = "../config"}
 servo_rand = {path = "../rand"}
 servo_remutex = {path = "../remutex"}
 servo_url = {path = "../url"}
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -65,18 +65,18 @@
 //! Since there is only one constellation, and its responsibilities include crash reporting,
 //! it is very important that it does not panic.
 
 use backtrace::Backtrace;
 use bluetooth_traits::BluetoothRequest;
 use browsingcontext::{BrowsingContext, SessionHistoryChange, SessionHistoryEntry};
 use browsingcontext::{FullyActiveBrowsingContextsIterator, AllBrowsingContextsIterator};
 use canvas::canvas_paint_thread::CanvasPaintThread;
-use canvas::webgl_paint_thread::WebGLPaintThread;
-use canvas_traits::CanvasMsg;
+use canvas::webgl_thread::WebGLThreads;
+use canvas_traits::canvas::CanvasMsg;
 use clipboard::{ClipboardContext, ClipboardProvider};
 use compositing::SendableFrameTree;
 use compositing::compositor_thread::CompositorProxy;
 use compositing::compositor_thread::Msg as ToCompositorMsg;
 use debugger;
 use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg};
 use euclid::{Size2D, TypedSize2D, ScaleFactor};
 use event_loop::EventLoop;
@@ -91,17 +91,16 @@ use log::{Log, LogLevel, LogLevelFilter,
 use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, FrameType, PipelineId};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
 use net_traits::{self, IpcSend, FetchResponseMsg, ResourceThreads};
 use net_traits::pub_domains::reg_host;
 use net_traits::request::RequestInit;
 use net_traits::storage_thread::{StorageThreadMsg, StorageType};
 use network_listener::NetworkListener;
-use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use pipeline::{InitialPipelineState, Pipeline};
 use profile_traits::mem;
 use profile_traits::time;
 use script_traits::{AnimationState, AnimationTickType, CompositorEvent};
 use script_traits::{ConstellationControlMsg, ConstellationMsg as FromCompositorMsg, DiscardBrowsingContext};
 use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData};
 use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg};
 use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
@@ -293,18 +292,21 @@ pub struct Constellation<Message, LTF, S
 
     /// The random number generator and probability for closing pipelines.
     /// This is for testing the hardening of the constellation.
     random_pipeline_closure: Option<(ServoRng, f32)>,
 
     /// Phantom data that keeps the Rust type system happy.
     phantom: PhantomData<(Message, LTF, STF)>,
 
+    /// Entry point to create and get channels to a WebGLThread.
+    webgl_threads: WebGLThreads,
+
     /// A channel through which messages can be sent to the webvr thread.
-    webvr_thread: Option<IpcSender<WebVRMsg>>,
+    webvr_chan: Option<IpcSender<WebVRMsg>>,
 }
 
 /// State needed to construct a constellation.
 pub struct InitialConstellationState {
     /// A channel through which messages can be sent to the compositor.
     pub compositor_proxy: CompositorProxy,
 
     /// A channel to the debugger, if applicable.
@@ -332,16 +334,22 @@ pub struct InitialConstellationState {
     pub mem_profiler_chan: mem::ProfilerChan,
 
     /// Webrender document ID.
     pub webrender_document: webrender_api::DocumentId,
 
     /// Webrender API.
     pub webrender_api_sender: webrender_api::RenderApiSender,
 
+    /// Entry point to create and get channels to a WebGLThread.
+    pub webgl_threads: WebGLThreads,
+
+    /// A channel to the webgl thread.
+    pub webvr_chan: Option<IpcSender<WebVRMsg>>,
+
     /// Whether the constellation supports the clipboard.
     /// TODO: this field is not used, remove it?
     pub supports_clipboard: bool,
 }
 
 /// Data needed for webdriver
 struct WebDriverData {
     load_channel: Option<(PipelineId, IpcSender<webdriver_msg::LoadStatus>)>,
@@ -576,17 +584,18 @@ impl<Message, LTF, STF> Constellation<Me
                 handled_warnings: VecDeque::new(),
                 random_pipeline_closure: opts::get().random_pipeline_closure_probability.map(|prob| {
                     let seed = opts::get().random_pipeline_closure_seed.unwrap_or_else(random);
                     let rng = ServoRng::from_seed(&[seed]);
                     warn!("Randomly closing pipelines.");
                     info!("Using seed {} for random pipeline closure.", seed);
                     (rng, prob)
                 }),
-                webvr_thread: None
+                webgl_threads: state.webgl_threads,
+                webvr_chan: state.webvr_chan,
             };
 
             constellation.run();
         }).expect("Thread spawning failed");
 
         (compositor_sender, swmanager_sender)
     }
 
@@ -695,17 +704,18 @@ impl<Message, LTF, STF> Constellation<Me
             event_loop,
             load_data,
             device_pixel_ratio: self.window_size.device_pixel_ratio,
             pipeline_namespace_id: self.next_pipeline_namespace_id(),
             prev_visibility,
             webrender_api_sender: self.webrender_api_sender.clone(),
             webrender_document: self.webrender_document,
             is_private,
-            webvr_thread: self.webvr_thread.clone()
+            webgl_chan: self.webgl_threads.pipeline(),
+            webvr_chan: self.webvr_chan.clone()
         });
 
         let pipeline = match result {
             Ok(result) => result,
             Err(e) => return self.handle_send_error(pipeline_id, e),
         };
 
         if let Some(host) = host {
@@ -989,20 +999,16 @@ impl<Message, LTF, STF> Constellation<Me
             }
             FromCompositorMsg::Reload(top_level_browsing_context_id) => {
                 debug!("constellation got reload message");
                 self.handle_reload_msg(top_level_browsing_context_id);
             }
             FromCompositorMsg::LogEntry(top_level_browsing_context_id, thread_name, entry) => {
                 self.handle_log_entry(top_level_browsing_context_id, thread_name, entry);
             }
-            FromCompositorMsg::SetWebVRThread(webvr_thread) => {
-                assert!(self.webvr_thread.is_none());
-                self.webvr_thread = Some(webvr_thread)
-            }
             FromCompositorMsg::WebVREvents(pipeline_ids, events) => {
                 debug!("constellation got {:?} WebVR events", events.len());
                 self.handle_webvr_events(pipeline_ids, events);
             }
         }
     }
 
     fn handle_request_from_script(&mut self, message: (PipelineId, FromScriptMsg)) {
@@ -1149,20 +1155,16 @@ impl<Message, LTF, STF> Constellation<Me
                 if source_is_top_level_pipeline {
                     self.compositor_proxy.send(ToCompositorMsg::HeadParsed(source_top_ctx_id));
                 }
             }
             FromScriptMsg::CreateCanvasPaintThread(size, sender) => {
                 debug!("constellation got create-canvas-paint-thread message");
                 self.handle_create_canvas_paint_thread_msg(&size, sender)
             }
-            FromScriptMsg::CreateWebGLPaintThread(size, attributes, sender) => {
-                debug!("constellation got create-WebGL-paint-thread message");
-                self.handle_create_webgl_paint_thread_msg(&size, attributes, sender)
-            }
             FromScriptMsg::NodeStatus(message) => {
                 debug!("constellation got NodeStatus message");
                 self.compositor_proxy.send(ToCompositorMsg::Status(source_top_ctx_id, message));
             }
             FromScriptMsg::SetDocumentState(state) => {
                 debug!("constellation got SetDocumentState message");
                 self.document_states.insert(source_pipeline_id, state);
             }
@@ -1362,17 +1364,22 @@ impl<Message, LTF, STF> Constellation<Me
 
         debug!("Exiting service worker manager thread.");
         if let Some(mgr) = self.swmanager_chan.as_ref() {
             if let Err(e) = mgr.send(ServiceWorkerMsg::Exit) {
                 warn!("Exit service worker manager failed ({})", e);
             }
         }
 
-        if let Some(chan) = self.webvr_thread.as_ref() {
+        debug!("Exiting WebGL thread.");
+        if let Err(e) = self.webgl_threads.exit() {
+            warn!("Exit WebGL Thread failed ({})", e);
+        }
+
+        if let Some(chan) = self.webvr_chan.as_ref() {
             debug!("Exiting WebVR thread.");
             if let Err(e) = chan.send(WebVRMsg::Exit) {
                 warn!("Exit WebVR thread failed ({})", e);
             }
         }
 
         debug!("Exiting timer scheduler.");
         if let Err(e) = self.scheduler_chan.send(TimerSchedulerMsg::Exit) {
@@ -2130,29 +2137,16 @@ impl<Message, LTF, STF> Constellation<Me
         let webrender_api = self.webrender_api_sender.clone();
         let sender = CanvasPaintThread::start(*size, webrender_api,
                                               opts::get().enable_canvas_antialiasing);
         if let Err(e) = response_sender.send(sender) {
             warn!("Create canvas paint thread response failed ({})", e);
         }
     }
 
-    fn handle_create_webgl_paint_thread_msg(
-            &mut self,
-            size: &Size2D<i32>,
-            attributes: GLContextAttributes,
-            response_sender: IpcSender<Result<(IpcSender<CanvasMsg>, GLLimits), String>>) {
-        let webrender_api = self.webrender_api_sender.clone();
-        let response = WebGLPaintThread::start(*size, attributes, webrender_api);
-
-        if let Err(e) = response_sender.send(response) {
-            warn!("Create WebGL paint thread response failed ({})", e);
-        }
-    }
-
     fn handle_webdriver_msg(&mut self, msg: WebDriverCommandMsg) {
         // Find the script channel for the given parent pipeline,
         // and pass the event to that script thread.
         match msg {
             WebDriverCommandMsg::GetWindowSize(_, reply) => {
                let _ = reply.send(self.window_size);
             },
             WebDriverCommandMsg::SetWindowSize(top_level_browsing_context_id, size, reply) => {
--- a/servo/components/constellation/lib.rs
+++ b/servo/components/constellation/lib.rs
@@ -25,17 +25,16 @@ extern crate ipc_channel;
 extern crate itertools;
 extern crate layout_traits;
 #[macro_use]
 extern crate log;
 extern crate metrics;
 extern crate msg;
 extern crate net;
 extern crate net_traits;
-extern crate offscreen_gl_context;
 extern crate profile_traits;
 extern crate script_traits;
 #[macro_use] extern crate serde;
 extern crate servo_config;
 extern crate servo_rand;
 extern crate servo_remutex;
 extern crate servo_url;
 extern crate style_traits;
--- a/servo/components/constellation/pipeline.rs
+++ b/servo/components/constellation/pipeline.rs
@@ -1,13 +1,14 @@
 /* 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 bluetooth_traits::BluetoothRequest;
+use canvas_traits::webgl::WebGLPipeline;
 use compositing::CompositionPipeline;
 use compositing::CompositorProxy;
 use compositing::compositor_thread::Msg as CompositorMsg;
 use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
 use euclid::{TypedSize2D, ScaleFactor};
 use event_loop::EventLoop;
 use gfx::font_cache_thread::FontCacheThread;
 use ipc_channel::Error;
@@ -166,18 +167,22 @@ pub struct InitialPipelineState {
     /// Webrender api.
     pub webrender_api_sender: webrender_api::RenderApiSender,
 
     /// The ID of the document processed by this script thread.
     pub webrender_document: webrender_api::DocumentId,
 
     /// Whether this pipeline is considered private.
     pub is_private: bool,
+
+    /// A channel to the webgl thread.
+    pub webgl_chan: WebGLPipeline,
+
     /// A channel to the webvr thread.
-    pub webvr_thread: Option<IpcSender<WebVRMsg>>,
+    pub webvr_chan: Option<IpcSender<WebVRMsg>>,
 }
 
 impl Pipeline {
     /// Starts a layout thread, and possibly a script thread, in
     /// a new process if requested.
     pub fn spawn<Message, LTF, STF>(state: InitialPipelineState) -> Result<Pipeline, Error>
         where LTF: LayoutThreadFactory<Message=Message>,
               STF: ScriptThreadFactory<Message=Message>
@@ -265,17 +270,18 @@ impl Pipeline {
                     pipeline_port: pipeline_port,
                     pipeline_namespace_id: state.pipeline_namespace_id,
                     layout_content_process_shutdown_chan: layout_content_process_shutdown_chan,
                     layout_content_process_shutdown_port: layout_content_process_shutdown_port,
                     script_content_process_shutdown_chan: script_content_process_shutdown_chan,
                     script_content_process_shutdown_port: script_content_process_shutdown_port,
                     webrender_api_sender: state.webrender_api_sender,
                     webrender_document: state.webrender_document,
-                    webvr_thread: state.webvr_thread,
+                    webgl_chan: state.webgl_chan,
+                    webvr_chan: state.webvr_chan,
                 };
 
                 // Spawn the child process.
                 //
                 // Yes, that's all there is to it!
                 if opts::multiprocess() {
                     let _ = unprivileged_pipeline_content.spawn_multiprocess()?;
                 } else {
@@ -465,17 +471,18 @@ pub struct UnprivilegedPipelineContent {
     pipeline_port: IpcReceiver<LayoutControlMsg>,
     pipeline_namespace_id: PipelineNamespaceId,
     layout_content_process_shutdown_chan: IpcSender<()>,
     layout_content_process_shutdown_port: IpcReceiver<()>,
     script_content_process_shutdown_chan: IpcSender<()>,
     script_content_process_shutdown_port: IpcReceiver<()>,
     webrender_api_sender: webrender_api::RenderApiSender,
     webrender_document: webrender_api::DocumentId,
-    webvr_thread: Option<IpcSender<WebVRMsg>>,
+    webgl_chan: WebGLPipeline,
+    webvr_chan: Option<IpcSender<WebVRMsg>>,
 }
 
 impl UnprivilegedPipelineContent {
     pub fn start_all<Message, LTF, STF>(self, wait_for_completion: bool)
         where LTF: LayoutThreadFactory<Message=Message>,
               STF: ScriptThreadFactory<Message=Message>
     {
         let image_cache = Arc::new(ImageCacheImpl::new(self.webrender_api_sender.create_api()));
@@ -494,17 +501,18 @@ impl UnprivilegedPipelineContent {
             resource_threads: self.resource_threads,
             image_cache: image_cache.clone(),
             time_profiler_chan: self.time_profiler_chan.clone(),
             mem_profiler_chan: self.mem_profiler_chan.clone(),
             devtools_chan: self.devtools_chan,
             window_size: self.window_size,
             pipeline_namespace_id: self.pipeline_namespace_id,
             content_process_shutdown_chan: self.script_content_process_shutdown_chan,
-            webvr_thread: self.webvr_thread,
+            webgl_chan: self.webgl_chan,
+            webvr_chan: self.webvr_chan,
         }, self.load_data.clone());
 
         LTF::create(self.id,
                     self.top_level_browsing_context_id,
                     self.load_data.url,
                     self.parent_info.is_some(),
                     layout_pair,
                     self.pipeline_port,
--- a/servo/components/gfx/display_list/mod.rs
+++ b/servo/components/gfx/display_list/mod.rs
@@ -29,17 +29,17 @@ use std::collections::HashMap;
 use std::fmt;
 use std::sync::Arc;
 use style::computed_values::{border_style, image_rendering};
 use style::values::computed::Filter;
 use style_traits::cursor::Cursor;
 use text::TextRun;
 use text::glyph::ByteIndex;
 use webrender_api::{self, ClipAndScrollInfo, ClipId, ColorF, GradientStop, LocalClip};
-use webrender_api::{MixBlendMode, ScrollPolicy, ScrollSensitivity, TransformStyle, WebGLContextId};
+use webrender_api::{MixBlendMode, ScrollPolicy, ScrollSensitivity, TransformStyle};
 
 pub use style::dom::OpaqueNode;
 
 /// The factor that we multiply the blur radius by in order to inflate the boundaries of display
 /// items that involve a blur. This ensures that the display item boundaries include all the ink.
 pub static BLUR_INFLATION_FACTOR: i32 = 3;
 
 #[derive(HeapSizeOf, Deserialize, Serialize)]
@@ -594,17 +594,16 @@ impl ScrollRoot {
 
 
 /// One drawing command in the list.
 #[derive(Clone, Deserialize, HeapSizeOf, Serialize)]
 pub enum DisplayItem {
     SolidColor(Box<SolidColorDisplayItem>),
     Text(Box<TextDisplayItem>),
     Image(Box<ImageDisplayItem>),
-    WebGL(Box<WebGLDisplayItem>),
     Border(Box<BorderDisplayItem>),
     Gradient(Box<GradientDisplayItem>),
     RadialGradient(Box<RadialGradientDisplayItem>),
     Line(Box<LineDisplayItem>),
     BoxShadow(Box<BoxShadowDisplayItem>),
     PushTextShadow(Box<PushTextShadowDisplayItem>),
     PopTextShadow(Box<PopTextShadowDisplayItem>),
     Iframe(Box<IframeDisplayItem>),
@@ -924,24 +923,16 @@ pub struct ImageDisplayItem {
     /// The amount of space to add to the right and bottom part of each tile, when the image
     /// is tiled.
     pub tile_spacing: Size2D<Au>,
 
     /// The algorithm we should use to stretch the image. See `image_rendering` in CSS-IMAGES-3 §
     /// 5.3.
     pub image_rendering: image_rendering::T,
 }
-
-#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
-pub struct WebGLDisplayItem {
-    pub base: BaseDisplayItem,
-    pub context_id: WebGLContextId,
-}
-
-
 /// Paints an iframe.
 #[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
 pub struct IframeDisplayItem {
     pub base: BaseDisplayItem,
     pub iframe: PipelineId,
 }
 
 /// Paints a gradient.
@@ -1245,17 +1236,16 @@ pub enum BoxShadowClipMode {
 }
 
 impl DisplayItem {
     pub fn base(&self) -> &BaseDisplayItem {
         match *self {
             DisplayItem::SolidColor(ref solid_color) => &solid_color.base,
             DisplayItem::Text(ref text) => &text.base,
             DisplayItem::Image(ref image_item) => &image_item.base,
-            DisplayItem::WebGL(ref webgl_item) => &webgl_item.base,
             DisplayItem::Border(ref border) => &border.base,
             DisplayItem::Gradient(ref gradient) => &gradient.base,
             DisplayItem::RadialGradient(ref gradient) => &gradient.base,
             DisplayItem::Line(ref line) => &line.base,
             DisplayItem::BoxShadow(ref box_shadow) => &box_shadow.base,
             DisplayItem::PushTextShadow(ref push_text_shadow) => &push_text_shadow.base,
             DisplayItem::PopTextShadow(ref pop_text_shadow) => &pop_text_shadow.base,
             DisplayItem::Iframe(ref iframe) => &iframe.base,
@@ -1371,17 +1361,16 @@ impl fmt::Debug for DisplayItem {
                             solid_color.color.b,
                             solid_color.color.a),
                 DisplayItem::Text(ref text) => {
                     format!("Text ({:?})",
                             &text.text_run.text[
                                 text.range.begin().0 as usize..(text.range.begin().0 + text.range.length().0) as usize])
                 }
                 DisplayItem::Image(_) => "Image".to_owned(),
-                DisplayItem::WebGL(_) => "WebGL".to_owned(),
                 DisplayItem::Border(_) => "Border".to_owned(),
                 DisplayItem::Gradient(_) => "Gradient".to_owned(),
                 DisplayItem::RadialGradient(_) => "RadialGradient".to_owned(),
                 DisplayItem::Line(_) => "Line".to_owned(),
                 DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(),
                 DisplayItem::PushTextShadow(_) => "PushTextShadow".to_owned(),
                 DisplayItem::PopTextShadow(_) => "PopTextShadow".to_owned(),
                 DisplayItem::Iframe(_) => "Iframe".to_owned(),
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -7,33 +7,33 @@
 //! Other browser engines sometimes call this "painting", but it is more accurately called display
 //! list building, as the actual painting does not happen here—only deciding *what* we're going to
 //! paint.
 
 #![deny(unsafe_code)]
 
 use app_units::{AU_PER_PX, Au};
 use block::{BlockFlow, BlockStackingContextType};
-use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg};
+use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg};
 use context::LayoutContext;
 use euclid::{Transform3D, Point2D, Vector2D, Rect, SideOffsets2D, Size2D, TypedSize2D};
 use flex::FlexFlow;
 use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
 use flow_ref::FlowRef;
-use fragment::{CoordinateSystem, Fragment, ImageFragmentInfo, ScannedTextFragmentInfo};
+use fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ImageFragmentInfo, ScannedTextFragmentInfo};
 use fragment::{SpecificFragmentInfo, TruncatedFragmentInfo};
 use gfx::display_list;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDetails, BorderDisplayItem};
 use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
 use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
 use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageBorder, ImageDisplayItem};
 use gfx::display_list::{LineDisplayItem, NormalBorder, OpaqueNode, PushTextShadowDisplayItem};
 use gfx::display_list::{PopTextShadowDisplayItem, RadialGradientDisplayItem, ScrollRoot};
 use gfx::display_list::{ScrollRootType, SolidColorDisplayItem, StackingContext, StackingContextType};
-use gfx::display_list::{TextDisplayItem, TextOrientation, WebGLDisplayItem, WebRenderImageInfo};
+use gfx::display_list::{TextDisplayItem, TextOrientation, WebRenderImageInfo};
 use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId};
 use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
 use ipc_channel::ipc;
 use list_item::ListItemFlow;
 use model::{self, MaybeAuto};
 use msg::constellation_msg::BrowsingContextId;
 use net_traits::image::base::PixelFormat;
 use net_traits::image_cache::UsePlaceholder;
@@ -1973,56 +1973,53 @@ impl FragmentDisplayListBuilding for Fra
                         image_rendering: self.style.get_inheritedbox().image_rendering.clone(),
                     }));
                 }
             }
             SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
                 let computed_width = canvas_fragment_info.dom_width.to_px();
                 let computed_height = canvas_fragment_info.dom_height.to_px();
 
-                let canvas_data = match canvas_fragment_info.ipc_renderer {
-                    Some(ref ipc_renderer) => {
-                        let ipc_renderer = ipc_renderer.lock().unwrap();
-                        let (sender, receiver) = ipc::channel().unwrap();
-                        ipc_renderer.send(CanvasMsg::FromLayout(
-                            FromLayoutMsg::SendData(sender))).unwrap();
-                        receiver.recv().unwrap()
+                let (image_key, format) = match canvas_fragment_info.source {
+                    CanvasFragmentSource::WebGL(image_key) => {
+                        (image_key, PixelFormat::BGRA8)
                     },
-                    None => return,
+                    CanvasFragmentSource::Image(ref ipc_renderer) => {
+                        match *ipc_renderer {
+                            Some(ref ipc_renderer) => {
+                                let ipc_renderer = ipc_renderer.lock().unwrap();
+                                let (sender, receiver) = ipc::channel().unwrap();
+                                ipc_renderer.send(CanvasMsg::FromLayout(
+                                    FromLayoutMsg::SendData(sender))).unwrap();
+                                (receiver.recv().unwrap().image_key, PixelFormat::BGRA8)
+                            },
+                            None => return,
+                        }
+                    }
                 };
 
                 let base = state.create_base_display_item(
                     &stacking_relative_content_box,
                     build_local_clip(&self.style),
                     self.node,
                     self.style.get_cursor(Cursor::Default),
                     DisplayListSection::Content);
-                let display_item = match canvas_data {
-                    CanvasData::Image(canvas_data) => {
-                        DisplayItem::Image(box ImageDisplayItem {
-                            base: base,
-                            webrender_image: WebRenderImageInfo {
-                                width: computed_width as u32,
-                                height: computed_height as u32,
-                                format: PixelFormat::BGRA8,
-                                key: Some(canvas_data.image_key),
-                            },
-                            image_data: None,
-                            stretch_size: stacking_relative_content_box.size,
-                            tile_spacing: Size2D::zero(),
-                            image_rendering: image_rendering::T::auto,
-                        })
-                    }
-                    CanvasData::WebGL(context_id) => {
-                        DisplayItem::WebGL(box WebGLDisplayItem {
-                            base: base,
-                            context_id: context_id,
-                        })
-                    }
-                };
+                let display_item = DisplayItem::Image(box ImageDisplayItem {
+                    base: base,
+                    webrender_image: WebRenderImageInfo {
+                        width: computed_width as u32,
+                        height: computed_height as u32,
+                        format: format,
+                        key: Some(image_key),
+                    },
+                    image_data: None,
+                    stretch_size: stacking_relative_content_box.size,
+                    tile_spacing: Size2D::zero(),
+                    image_rendering: image_rendering::T::auto,
+                });
 
                 state.add_display_item(display_item);
             }
             SpecificFragmentInfo::UnscannedText(_) => {
                 panic!("Shouldn't see unscanned fragments here.")
             }
             SpecificFragmentInfo::TableColumn(_) => {
                 panic!("Shouldn't see table column fragments here.")
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The `Fragment` type, which represents the leaves of the layout tree.
 
 #![deny(unsafe_code)]
 
 use ServoArc;
 use app_units::Au;
-use canvas_traits::CanvasMsg;
+use canvas_traits::canvas::CanvasMsg;
 use context::{LayoutContext, with_thread_local_font_context};
 use euclid::{Transform3D, Point2D, Vector2D, Radians, Rect, Size2D};
 use floats::ClearType;
 use flow::{self, ImmutableFlowUtils};
 use flow_ref::FlowRef;
 use gfx;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
 use gfx::text::glyph::ByteIndex;
@@ -25,17 +25,17 @@ use ipc_channel::ipc::IpcSender;
 #[cfg(debug_assertions)]
 use layout_debug;
 use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint};
 use model::{style_length, ToGfxMatrix};
 use msg::constellation_msg::{BrowsingContextId, PipelineId};
 use net_traits::image::base::{Image, ImageMetadata};
 use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
 use range::*;
-use script_layout_interface::HTMLCanvasData;
+use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
 use script_layout_interface::SVGSVGData;
 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 use servo_url::ServoUrl;
 use std::{f32, fmt};
 use std::borrow::ToOwned;
 use std::cmp::{Ordering, max, min};
 use std::collections::LinkedList;
@@ -48,16 +48,17 @@ use style::logical_geometry::{Direction,
 use style::properties::ComputedValues;
 use style::selector_parser::RestyleDamage;
 use style::servo::restyle_damage::RECONSTRUCT_FLOW;
 use style::str::char_is_whitespace;
 use style::values::{self, Either, Auto};
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 use text;
 use text::TextRunScanner;
+use webrender_api;
 use wrapper::ThreadSafeLayoutNodeHelpers;
 
 // From gfxFontConstants.h in Firefox.
 static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
 static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
 
 // https://drafts.csswg.org/css-images/#default-object-size
 static DEFAULT_REPLACED_WIDTH: i32 = 300;
@@ -317,27 +318,41 @@ impl InlineAbsoluteFragmentInfo {
     pub fn new(flow_ref: FlowRef) -> InlineAbsoluteFragmentInfo {
         InlineAbsoluteFragmentInfo {
             flow_ref: flow_ref,
         }
     }
 }
 
 #[derive(Clone)]
+pub enum CanvasFragmentSource {
+    WebGL(webrender_api::ImageKey),
+    Image(Option<Arc<Mutex<IpcSender<CanvasMsg>>>>)
+}
+
+#[derive(Clone)]
 pub struct CanvasFragmentInfo {
-    pub ipc_renderer: Option<Arc<Mutex<IpcSender<CanvasMsg>>>>,
+    pub source: CanvasFragmentSource,
     pub dom_width: Au,
     pub dom_height: Au,
 }
 
 impl CanvasFragmentInfo {
     pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
+        let source = match data.source {
+            HTMLCanvasDataSource::WebGL(texture_id) => {
+                CanvasFragmentSource::WebGL(texture_id)
+            },
+            HTMLCanvasDataSource::Image(ipc_sender) => {
+                CanvasFragmentSource::Image(ipc_sender.map(|renderer| Arc::new(Mutex::new(renderer))))
+            }
+        };
+
         CanvasFragmentInfo {
-            ipc_renderer: data.ipc_renderer
-                              .map(|renderer| Arc::new(Mutex::new(renderer))),
+            source: source,
             dom_width: Au::from_px(data.width as i32),
             dom_height: Au::from_px(data.height as i32),
         }
     }
 }
 
 #[derive(Clone)]
 pub struct SvgFragmentInfo {
--- a/servo/components/layout/webrender_helpers.rs
+++ b/servo/components/layout/webrender_helpers.rs
@@ -300,21 +300,16 @@ impl WebRenderDisplayItemConverter for D
                                            Some(item.base.local_clip),
                                            item.stretch_size.to_sizef(),
                                            item.tile_spacing.to_sizef(),
                                            item.image_rendering.to_image_rendering(),
                                            id);
                     }
                 }
             }
-            DisplayItem::WebGL(ref item) => {
-                builder.push_webgl_canvas(item.base.bounds.to_rectf(),
-                                          Some(item.base.local_clip),
-                                          item.context_id);
-            }
             DisplayItem::Border(ref item) => {
                 let rect = item.base.bounds.to_rectf();
                 let widths = item.border_widths.to_border_widths();
 
                 let details = match item.details {
                     BorderDetails::Normal(ref border) => {
                         let left = webrender_api::BorderSide {
                             color: border.color.left,
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -25,18 +25,21 @@
 //!    will add the object to the graph, and will trace that object as well.
 //! 5. When the GC finishes tracing, it [`finalizes`](../index.html#destruction)
 //!    any reflectors that were not reachable.
 //!
 //! The `unsafe_no_jsmanaged_fields!()` macro adds an empty implementation of
 //! `JSTraceable` to a datatype.
 
 use app_units::Au;
-use canvas_traits::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
-use canvas_traits::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
+use canvas_traits::canvas::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
+use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
+use canvas_traits::webgl::{WebGLBufferId, WebGLFramebufferId, WebGLProgramId, WebGLRenderbufferId};
+use canvas_traits::webgl::{WebGLChan, WebGLContextShareMode, WebGLError, WebGLPipeline, WebGLMsgSender};
+use canvas_traits::webgl::{WebGLReceiver, WebGLSender, WebGLShaderId, WebGLTextureId, WebGLVertexArrayId};
 use cssparser::RGBA;
 use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
 use dom::abstractworker::SharedRt;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::js::{JS, Root};
 use dom::bindings::refcounted::{Trusted, TrustedPromise};
 use dom::bindings::reflector::{DomObject, Reflector};
 use dom::bindings::str::{DOMString, USVString};
@@ -100,18 +103,17 @@ use style::properties::PropertyDeclarati
 use style::selector_parser::{PseudoElement, Snapshot};
 use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
 use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule};
 use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule, ViewportRule};
 use style::stylesheets::keyframes_rule::Keyframe;
 use style::values::specified::Length;
 use time::Duration;
 use uuid::Uuid;
-use webrender_api::{WebGLBufferId, WebGLError, WebGLFramebufferId, WebGLProgramId};
-use webrender_api::{WebGLRenderbufferId, WebGLShaderId, WebGLTextureId, WebGLVertexArrayId};
+use webrender_api::ImageKey;
 use webvr_traits::WebVRGamepadHand;
 
 /// A trait to allow tracing (only) DOM objects.
 pub unsafe trait JSTraceable {
     /// Trace `self`.
     unsafe fn trace(&self, trc: *mut JSTracer);
 }
 
@@ -386,18 +388,23 @@ unsafe_no_jsmanaged_fields!(ResourceThre
 unsafe_no_jsmanaged_fields!(StatusCode);
 unsafe_no_jsmanaged_fields!(SystemTime);
 unsafe_no_jsmanaged_fields!(Instant);
 unsafe_no_jsmanaged_fields!(RelativePos);
 unsafe_no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
 unsafe_no_jsmanaged_fields!(PathBuf);
 unsafe_no_jsmanaged_fields!(CSSErrorReporter);
 unsafe_no_jsmanaged_fields!(DrawAPaintImageResult);
+unsafe_no_jsmanaged_fields!(ImageKey);
 unsafe_no_jsmanaged_fields!(WebGLBufferId);
+unsafe_no_jsmanaged_fields!(WebGLChan);
+unsafe_no_jsmanaged_fields!(WebGLContextShareMode);
 unsafe_no_jsmanaged_fields!(WebGLFramebufferId);
+unsafe_no_jsmanaged_fields!(WebGLMsgSender);
+unsafe_no_jsmanaged_fields!(WebGLPipeline);
 unsafe_no_jsmanaged_fields!(WebGLProgramId);
 unsafe_no_jsmanaged_fields!(WebGLRenderbufferId);
 unsafe_no_jsmanaged_fields!(WebGLShaderId);
 unsafe_no_jsmanaged_fields!(WebGLTextureId);
 unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
 unsafe_no_jsmanaged_fields!(MediaList);
 unsafe_no_jsmanaged_fields!(WebVRGamepadHand);
 unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
@@ -461,16 +468,30 @@ unsafe impl<T: Send> JSTraceable for Rec
 
 unsafe impl<T: Send> JSTraceable for Sender<T> {
     #[inline]
     unsafe fn trace(&self, _: *mut JSTracer) {
         // Do nothing
     }
 }
 
+unsafe impl<T: Send> JSTraceable for WebGLReceiver<T> where T: for<'de> Deserialize<'de> + Serialize {
+    #[inline]
+    unsafe fn trace(&self, _: *mut JSTracer) {
+        // Do nothing
+    }
+}
+
+unsafe impl<T: Send> JSTraceable for WebGLSender<T> where T: for<'de> Deserialize<'de> + Serialize {
+    #[inline]
+    unsafe fn trace(&self, _: *mut JSTracer) {
+        // Do nothing
+    }
+}
+
 unsafe impl JSTraceable for Transform2D<f32> {
     #[inline]
     unsafe fn trace(&self, _trc: *mut JSTracer) {
         // Do nothing
     }
 }
 
 unsafe impl JSTraceable for Transform3D<f64> {
--- a/servo/components/script/dom/canvasgradient.rs
+++ b/servo/components/script/dom/canvasgradient.rs
@@ -1,13 +1,13 @@
 /* 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::{CanvasGradientStop, FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle};
+use canvas_traits::canvas::{CanvasGradientStop, FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle};
 use cssparser::{Parser, ParserInput, RGBA};
 use cssparser::Color as CSSColor;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::CanvasGradientBinding;
 use dom::bindings::codegen::Bindings::CanvasGradientBinding::CanvasGradientMethods;
 use dom::bindings::error::{Error, ErrorResult};
 use dom::bindings::js::Root;
 use dom::bindings::num::Finite;
--- a/servo/components/script/dom/canvaspattern.rs
+++ b/servo/components/script/dom/canvaspattern.rs
@@ -1,13 +1,13 @@
 /* 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::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle};
+use canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle};
 use dom::bindings::codegen::Bindings::CanvasPatternBinding;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::{Reflector, reflect_dom_object};
 use dom::canvasgradient::ToFillOrStrokeStyle;
 use dom::globalscope::GlobalScope;
 use dom_struct::dom_struct;
 use euclid::Size2D;
 
--- a/servo/components/script/dom/canvasrenderingcontext2d.rs
+++ b/servo/components/script/dom/canvasrenderingcontext2d.rs
@@ -1,16 +1,16 @@
 /* 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::{Canvas2dMsg, CanvasCommonMsg, CanvasMsg};
-use canvas_traits::{CompositionOrBlending, FillOrStrokeStyle, FillRule};
-use canvas_traits::{LineCapStyle, LineJoinStyle, LinearGradientStyle};
-use canvas_traits::{RadialGradientStyle, RepetitionStyle, byte_swap_and_premultiply};
+use canvas_traits::canvas::{Canvas2dMsg, CanvasMsg};
+use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule};
+use canvas_traits::canvas::{LineCapStyle, LineJoinStyle, LinearGradientStyle};
+use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle, byte_swap_and_premultiply};
 use cssparser::{Parser, ParserInput, RGBA};
 use cssparser::Color as CSSColor;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap;
@@ -158,30 +158,26 @@ impl CanvasRenderingContext2D {
         let boxed = box CanvasRenderingContext2D::new_inherited(global, Some(canvas), image_cache, base_url, size);
         reflect_dom_object(boxed, global, CanvasRenderingContext2DBinding::Wrap)
     }
 
     // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
     pub fn set_bitmap_dimensions(&self, size: Size2D<i32>) {
         self.reset_to_initial_state();
         self.ipc_renderer
-            .send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size)))
+            .send(CanvasMsg::Recreate(size))
             .unwrap();
     }
 
     // https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
     fn reset_to_initial_state(&self) {
         self.saved_states.borrow_mut().clear();
         *self.state.borrow_mut() = CanvasContextState::new();
     }
 
-    pub fn ipc_renderer(&self) -> IpcSender<CanvasMsg> {
-        self.ipc_renderer.clone()
-    }
-
     fn mark_as_dirty(&self) {
         if let Some(ref canvas) = self.canvas {
             canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
         }
     }
 
     fn update_transform(&self) {
         self.ipc_renderer
@@ -1387,17 +1383,17 @@ impl CanvasRenderingContext2DMethods for
                 .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowColor(color)))
                 .unwrap()
         }
     }
 }
 
 impl Drop for CanvasRenderingContext2D {
     fn drop(&mut self) {
-        if let Err(err) = self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Close)) {
+        if let Err(err) = self.ipc_renderer.send(CanvasMsg::Close) {
             warn!("Could not close canvas: {}", err)
         }
     }
 }
 
 pub fn parse_color(string: &str) -> Result<RGBA, ()> {
     let mut input = ParserInput::new(string);
     let mut parser = Parser::new(&mut input);
--- a/servo/components/script/dom/htmlcanvaselement.rs
+++ b/servo/components/script/dom/htmlcanvaselement.rs
@@ -1,14 +1,14 @@
 /* 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 base64;
-use canvas_traits::{CanvasMsg, FromScriptMsg};
+use canvas_traits::canvas::{CanvasMsg, FromScriptMsg};
 use dom::attr::Attr;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
 use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
 use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
 use dom::bindings::codegen::UnionTypes::CanvasRenderingContext2DOrWebGLRenderingContext;
 use dom::bindings::conversions::ConversionResult;
@@ -25,21 +25,21 @@ use dom::htmlelement::HTMLElement;
 use dom::node::{Node, window_from_node};
 use dom::virtualmethods::VirtualMethods;
 use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext};
 use dom_struct::dom_struct;
 use euclid::Size2D;
 use html5ever::{LocalName, Prefix};
 use image::ColorType;
 use image::png::PNGEncoder;
-use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::ipc;
 use js::error::throw_type_error;
 use js::jsapi::{HandleValue, JSContext};
 use offscreen_gl_context::GLContextAttributes;
-use script_layout_interface::HTMLCanvasData;
+use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
 use std::iter::repeat;
 use style::attr::{AttrValue, LengthOrPercentageOrAuto};
 
 const DEFAULT_WIDTH: u32 = 300;
 const DEFAULT_HEIGHT: u32 = 150;
 
 #[must_root]
 #[derive(JSTraceable, Clone, HeapSizeOf)]
@@ -101,31 +101,32 @@ pub trait LayoutHTMLCanvasElementHelpers
     fn get_height(&self) -> LengthOrPercentageOrAuto;
 }
 
 impl LayoutHTMLCanvasElementHelpers for LayoutJS<HTMLCanvasElement> {
     #[allow(unsafe_code)]
     fn data(&self) -> HTMLCanvasData {
         unsafe {
             let canvas = &*self.unsafe_get();
-            let ipc_renderer = canvas.context.borrow_for_layout().as_ref().map(|context| {
-                match *context {
-                    CanvasContext::Context2d(ref context) => {
-                        context.to_layout().get_ipc_renderer()
-                    },
-                    CanvasContext::WebGL(ref context) => {
-                        context.to_layout().get_ipc_renderer()
-                    },
+            let source = match canvas.context.borrow_for_layout().as_ref() {
+                Some(&CanvasContext::Context2d(ref context)) => {
+                    HTMLCanvasDataSource::Image(Some(context.to_layout().get_ipc_renderer()))
+                },
+                Some(&CanvasContext::WebGL(ref context)) => {
+                    context.to_layout().canvas_data_source()
+                },
+                None => {
+                    HTMLCanvasDataSource::Image(None)
                 }
-            });
+            };
 
             let width_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("width"));
             let height_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("height"));
             HTMLCanvasData {
-                ipc_renderer: ipc_renderer,
+                source: source,
                 width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
                 height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
             }
         }
     }
 
     #[allow(unsafe_code)]
     fn get_width(&self) -> LengthOrPercentageOrAuto {
@@ -145,25 +146,16 @@ impl LayoutHTMLCanvasElementHelpers for 
                 .map(AttrValue::as_uint_px_dimension)
                 .unwrap_or(LengthOrPercentageOrAuto::Auto)
         }
     }
 }
 
 
 impl HTMLCanvasElement {
-    pub fn ipc_renderer(&self) -> Option<IpcSender<CanvasMsg>> {
-        self.context.borrow().as_ref().map(|context| {
-            match *context {
-                CanvasContext::Context2d(ref context) => context.ipc_renderer(),
-                CanvasContext::WebGL(ref context) => context.ipc_renderer(),
-            }
-        })
-    }
-
     pub fn get_or_init_2d_context(&self) -> Option<Root<CanvasRenderingContext2D>> {
         if self.context.borrow().is_none() {
             let window = window_from_node(self);
             let size = self.get_size();
             let context = CanvasRenderingContext2D::new(window.upcast::<GlobalScope>(), self, size);
             *self.context.borrow_mut() = Some(CanvasContext::Context2d(JS::from_ref(&*context)));
         }
 
@@ -216,32 +208,36 @@ impl HTMLCanvasElement {
 
     pub fn fetch_all_data(&self) -> Option<(Vec<u8>, Size2D<i32>)> {
         let size = self.get_size();
 
         if size.width == 0 || size.height == 0 {
             return None
         }
 
-        let data = if let Some(renderer) = self.ipc_renderer() {
-            let (sender, receiver) = ipc::channel().unwrap();
-            let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender));
-            renderer.send(msg).unwrap();
+        let data = match self.context.borrow().as_ref() {
+            Some(&CanvasContext::Context2d(ref context)) => {
+                let (sender, receiver) = ipc::channel().unwrap();
+                let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender));
+                context.get_ipc_renderer().send(msg).unwrap();
 
-            match receiver.recv().unwrap() {
-                Some(pixels) => pixels,
-                None => {
-                    // TODO(emilio, #14109): Not sure if WebGL canvas is
-                    // required for 2d spec, but I think it's not, if so, make
-                    // this return a readback from the GL context.
-                    return None;
+                match receiver.recv().unwrap() {
+                    Some(pixels) => pixels,
+                    None => {
+                        return None;
+                    }
                 }
+            },
+            Some(&CanvasContext::WebGL(_)) => {
+                // TODO: add a method in WebGLRenderingContext to get the pixels.
+                return None;
+            },
+            None => {
+                repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect()
             }
-        } else {
-            repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect()
         };
 
         Some((data, size))
     }
 }
 
 impl HTMLCanvasElementMethods for HTMLCanvasElement {
     // https://html.spec.whatwg.org/multipage/#dom-canvas-width
--- a/servo/components/script/dom/paintrenderingcontext2d.rs
+++ b/servo/components/script/dom/paintrenderingcontext2d.rs
@@ -1,15 +1,15 @@
 /* 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::CanvasData;
-use canvas_traits::CanvasMsg;
-use canvas_traits::FromLayoutMsg;
+use canvas_traits::canvas::CanvasImageData;
+use canvas_traits::canvas::CanvasMsg;
+use canvas_traits::canvas::FromLayoutMsg;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
 use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding;
 use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding::PaintRenderingContext2DMethods;
 use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue;
 use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
@@ -53,19 +53,19 @@ impl PaintRenderingContext2D {
     }
 
     pub fn new(global: &PaintWorkletGlobalScope) -> Root<PaintRenderingContext2D> {
         reflect_dom_object(box PaintRenderingContext2D::new_inherited(global),
                            global,
                            PaintRenderingContext2DBinding::Wrap)
     }
 
-    pub fn send_data(&self, sender: IpcSender<CanvasData>) {
+    pub fn send_data(&self, sender: IpcSender<CanvasImageData>) {
         let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender));
-        let _ = self.context.ipc_renderer().send(msg);
+        let _ = self.context.get_ipc_renderer().send(msg);
     }
 
     pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
         self.context.take_missing_image_urls()
     }
 
     pub fn set_bitmap_dimensions(&self,
                                  size: TypedSize2D<f32, CSSPixel>,
--- a/servo/components/script/dom/paintworkletglobalscope.rs
+++ b/servo/components/script/dom/paintworkletglobalscope.rs
@@ -1,13 +1,12 @@
 /* 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::CanvasData;
 use dom::bindings::callback::CallbackContainer;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
 use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding::PaintWorkletGlobalScopeMethods;
 use dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
 use dom::bindings::conversions::get_property;
 use dom::bindings::conversions::get_property_jsval;
 use dom::bindings::error::Error;
@@ -293,17 +292,17 @@ impl PaintWorkletGlobalScope {
             debug!("Paint function threw an exception {}.", name);
             unsafe { JS_ClearPendingException(cx); }
             return self.invalid_image(size_in_dpx, missing_image_urls);
         }
 
         let (sender, receiver) = ipc::channel().expect("IPC channel creation.");
         rendering_context.send_data(sender);
         let image_key = match receiver.recv() {
-            Ok(CanvasData::Image(data)) => Some(data.image_key),
+            Ok(data) => Some(data.image_key),
             _ => None,
         };
 
         DrawAPaintImageResult {
             width: size_in_dpx.width,
             height: size_in_dpx.height,
             format: PixelFormat::BGRA8,
             image_key: image_key,
--- a/servo/components/script/dom/vrdisplay.rs
+++ b/servo/components/script/dom/vrdisplay.rs
@@ -1,13 +1,13 @@
 /* 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::CanvasMsg;
+use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand};
 use core::ops::Deref;
 use dom::bindings::callback::ExceptionHandling;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceBinding::PerformanceMethods;
 use dom::bindings::codegen::Bindings::VRDisplayBinding;
 use dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods;
 use dom::bindings::codegen::Bindings::VRDisplayBinding::VREye;
 use dom::bindings::codegen::Bindings::VRLayerBinding::VRLayer;
@@ -27,28 +27,26 @@ use dom::promise::Promise;
 use dom::vrdisplaycapabilities::VRDisplayCapabilities;
 use dom::vrdisplayevent::VRDisplayEvent;
 use dom::vreyeparameters::VREyeParameters;
 use dom::vrframedata::VRFrameData;
 use dom::vrpose::VRPose;
 use dom::vrstageparameters::VRStageParameters;
 use dom::webglrenderingcontext::WebGLRenderingContext;
 use dom_struct::dom_struct;
-use ipc_channel::ipc;
-use ipc_channel::ipc::{IpcSender, IpcReceiver};
+use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::JSContext;
 use script_runtime::CommonScriptMsg;
 use script_runtime::ScriptThreadEventCategory::WebVREvent;
 use script_thread::Runnable;
 use std::cell::Cell;
 use std::mem;
 use std::rc::Rc;
 use std::sync::mpsc;
 use std::thread;
-use webrender_api::VRCompositorCommand;
 use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRLayer, WebVRMsg};
 
 #[dom_struct]
 pub struct VRDisplay {
     eventtarget: EventTarget,
     #[ignore_heap_size_of = "Defined in rust-webvr"]
     display: DOMRefCell<WebVRDisplayData>,
     depth_near: Cell<f64>,
@@ -66,17 +64,17 @@ pub struct VRDisplay {
     #[ignore_heap_size_of = "Defined in rust-webvr"]
     next_raf_id: Cell<u32>,
     /// List of request animation frame callbacks
     #[ignore_heap_size_of = "closures are hard"]
     raf_callback_list: DOMRefCell<Vec<(u32, Option<Rc<FrameRequestCallback>>)>>,
     // Compositor VRFrameData synchonization
     frame_data_status: Cell<VRFrameDataStatus>,
     #[ignore_heap_size_of = "channels are hard"]
-    frame_data_receiver: DOMRefCell<Option<IpcReceiver<Result<Vec<u8>, ()>>>>,
+    frame_data_receiver: DOMRefCell<Option<WebGLReceiver<Result<Vec<u8>, ()>>>>,
     running_display_raf: Cell<bool>,
     paused: Cell<bool>,
     stopped_on_pause: Cell<bool>,
 }
 
 unsafe_no_jsmanaged_fields!(WebVRDisplayData);
 unsafe_no_jsmanaged_fields!(WebVRFrameData);
 unsafe_no_jsmanaged_fields!(WebVRLayer);
@@ -381,21 +379,20 @@ impl VRDisplayMethods for VRDisplay {
 
     // https://w3c.github.io/webvr/#dom-vrdisplay-submitframe
     fn SubmitFrame(&self) {
         if !self.presenting.get() {
             warn!("VRDisplay not presenting");
             return;
         }
 
-        let api_sender = self.layer_ctx.get().unwrap().ipc_renderer();
-        let display_id = self.display.borrow().display_id as u64;
+        let display_id = self.display.borrow().display_id;
         let layer = self.layer.borrow();
-        let msg = VRCompositorCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds);
-        api_sender.send(CanvasMsg::WebVR(msg)).unwrap();
+        let msg = WebVRCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds);
+        self.layer_ctx.get().unwrap().send_vr_command(msg);
     }
 
     // https://w3c.github.io/webvr/spec/1.1/#dom-vrdisplay-getlayers
     fn GetLayers(&self) -> Vec<VRLayer> {
         // WebVR spec: MUST return an empty array if the VRDisplay is not currently presenting
         if !self.presenting.get() {
             return Vec::new();
         }
@@ -484,21 +481,21 @@ impl VRDisplay {
     fn notify_event(&self, event: &WebVRDisplayEvent) {
         let root = Root::from_ref(&*self);
         let event = VRDisplayEvent::new_from_webvr(&self.global(), &root, &event);
         event.upcast::<Event>().fire(self.global().upcast::<EventTarget>());
     }
 
     fn init_present(&self) {
         self.presenting.set(true);
-        let (sync_sender, sync_receiver) = ipc::channel().unwrap();
+        let (sync_sender, sync_receiver) = webgl_channel().unwrap();
         *self.frame_data_receiver.borrow_mut() = Some(sync_receiver);
 
-        let display_id = self.display.borrow().display_id as u64;
-        let api_sender = self.layer_ctx.get().unwrap().ipc_renderer();
+        let display_id = self.display.borrow().display_id;
+        let api_sender = self.layer_ctx.get().unwrap().webgl_sender();
         let js_sender = self.global().script_chan();
         let address = Trusted::new(&*self);
         let near_init = self.depth_near.get();
         let far_init = self.depth_far.get();
 
         // The render loop at native headset frame rate is implemented using a dedicated thread.
         // Every loop iteration syncs pose data with the HMD, submits the pixels to the display and waits for Vsync.
         // Both the requestAnimationFrame call of a VRDisplay in the JavaScript thread and the VRSyncPoses call
@@ -506,28 +503,28 @@ impl VRDisplay {
         // while the render thread is syncing the VRFrameData to be used for the current frame.
         // This thread runs until the user calls ExitPresent, the tab is closed or some unexpected error happened.
         thread::Builder::new().name("WebVR_RAF".into()).spawn(move || {
             let (raf_sender, raf_receiver) = mpsc::channel();
             let mut near = near_init;
             let mut far = far_init;
 
             // Initialize compositor
-            api_sender.send(CanvasMsg::WebVR(VRCompositorCommand::Create(display_id))).unwrap();
+            api_sender.send_vr(WebVRCommand::Create(display_id)).unwrap();
             loop {
                 // Run RAF callbacks on JavaScript thread
                 let msg = box NotifyDisplayRAF {
                     address: address.clone(),
                     sender: raf_sender.clone()
                 };
                 js_sender.send(CommonScriptMsg::RunnableMsg(WebVREvent, msg)).unwrap();
 
                 // Run Sync Poses in parallell on Render thread
-                let msg = VRCompositorCommand::SyncPoses(display_id, near, far, sync_sender.clone());
-                api_sender.send(CanvasMsg::WebVR(msg)).unwrap();
+                let msg = WebVRCommand::SyncPoses(display_id, near, far, sync_sender.clone());
+                api_sender.send_vr(msg).unwrap();
 
                 // Wait until both SyncPoses & RAF ends
                 if let Ok(depth) = raf_receiver.recv().unwrap() {
                     near = depth.0;
                     far = depth.1;
                 } else {
                     // Stop thread
                     // ExitPresent called or some error happened
@@ -536,20 +533,19 @@ impl VRDisplay {
             }
         }).expect("Thread spawning failed");
     }
 
     fn stop_present(&self) {
         self.presenting.set(false);
         *self.frame_data_receiver.borrow_mut() = None;
 
-        let api_sender = self.layer_ctx.get().unwrap().ipc_renderer();
-        let display_id = self.display.borrow().display_id as u64;
-        let msg = VRCompositorCommand::Release(display_id);
-        api_sender.send(CanvasMsg::WebVR(msg)).unwrap();
+        let api_sender = self.layer_ctx.get().unwrap().webgl_sender();
+        let display_id = self.display.borrow().display_id;
+        api_sender.send_vr(WebVRCommand::Release(display_id)).unwrap();
     }
 
     // Only called when the JSContext is destroyed while presenting.
     // In this case we don't want to wait for WebVR Thread response.
     fn force_stop_present(&self) {
         self.webvr_thread().send(WebVRMsg::ExitPresent(self.global().pipeline_id(),
                                                        self.display.borrow().display_id,
                                                        None))
@@ -622,17 +618,17 @@ impl Runnable for NotifyDisplayRAF {
 
     fn handler(self: Box<Self>) {
         let display = self.address.root();
         display.handle_raf(&self.sender);
     }
 }
 
 
-// WebVR Spect: If the number of values in the leftBounds/rightBounds arrays
+// WebVR Spec: If the number of values in the leftBounds/rightBounds arrays
 // is not 0 or 4 for any of the passed layers the promise is rejected
 fn parse_bounds(src: &Option<Vec<Finite<f32>>>, dst: &mut [f32; 4]) -> Result<(), &'static str> {
     match *src {
         Some(ref values) => {
             if values.len() == 0 {
                 return Ok(())
             }
             if values.len() != 4 {
--- a/servo/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs
+++ b/servo/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs
@@ -1,26 +1,25 @@
 /* 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::CanvasMsg;
+use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLError};
 use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::{self, OESVertexArrayObjectMethods};
 use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
 use dom::bindings::js::{JS, MutNullableJS, Root};
 use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::webglrenderingcontext::WebGLRenderingContext;
 use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
 use dom_struct::dom_struct;
 use js::conversions::ToJSValConvertible;
 use js::jsapi::JSContext;
 use js::jsval::{JSVal, NullValue};
 use std::iter;
 use super::{WebGLExtension, WebGLExtensions};
-use webrender_api::{self, WebGLCommand, WebGLError};
 
 #[dom_struct]
 pub struct OESVertexArrayObject {
     reflector_: Reflector,
     ctx: JS<WebGLRenderingContext>,
     bound_vao: MutNullableJS<WebGLVertexArrayObjectOES>,
 }
 
@@ -43,49 +42,49 @@ impl OESVertexArrayObject {
         }
         rval.get()
     }
 }
 
 impl OESVertexArrayObjectMethods for OESVertexArrayObject {
     // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
     fn CreateVertexArrayOES(&self) -> Option<Root<WebGLVertexArrayObjectOES>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ctx.send_renderer_message(CanvasMsg::WebGL(WebGLCommand::CreateVertexArray(sender)));
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.ctx.send_command(WebGLCommand::CreateVertexArray(sender));
 
         let result = receiver.recv().unwrap();
         result.map(|vao_id| WebGLVertexArrayObjectOES::new(&self.global(), vao_id))
     }
 
     // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
     fn DeleteVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
         if let Some(vao) = vao {
             if vao.is_deleted() {
                 return;
             }
 
             // Unbind deleted VAO if currently bound
             if let Some(bound_vao) = self.bound_vao.get() {
                 if bound_vao.id() == vao.id() {
                     self.bound_vao.set(None);
-                    self.ctx.send_renderer_message(CanvasMsg::WebGL(WebGLCommand::BindVertexArray(None)));
+                    self.ctx.send_command(WebGLCommand::BindVertexArray(None));
                 }
             }
 
             // Remove VAO references from buffers
             let buffers = vao.bound_attrib_buffers();
             for buffer in buffers {
                 buffer.remove_vao_reference(vao.id());
             }
             if let Some(buffer) = vao.bound_buffer_element_array() {
                 buffer.remove_vao_reference(vao.id());
             }
 
             // Delete the vao
-            self.ctx.send_renderer_message(CanvasMsg::WebGL(WebGLCommand::DeleteVertexArray(vao.id())));
+            self.ctx.send_command(WebGLCommand::DeleteVertexArray(vao.id()));
             vao.set_deleted();
         }
     }
 
     // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
     fn IsVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool {
         // Conformance tests expect false if vao never bound
         vao.map_or(false, |vao| !vao.is_deleted() && vao.ever_bound())
@@ -109,27 +108,27 @@ impl OESVertexArrayObjectMethods for OES
         }
 
         if let Some(vao) = vao {
             if vao.is_deleted() {
                 self.ctx.webgl_error(WebGLError::InvalidOperation);
                 return;
             }
 
-            self.ctx.send_renderer_message(CanvasMsg::WebGL(WebGLCommand::BindVertexArray(Some(vao.id()))));
+            self.ctx.send_command(WebGLCommand::BindVertexArray(Some(vao.id())));
             vao.set_ever_bound();
             self.bound_vao.set(Some(&vao));
 
             // Restore WebGLRenderingContext current bindings
             let buffers = vao.borrow_bound_attrib_buffers();
             self.ctx.set_bound_attrib_buffers(buffers.iter().map(|(k, v)| (*k, &**v)));
             let element_array = vao.bound_buffer_element_array();
             self.ctx.set_bound_buffer_element_array(element_array.as_ref().map(|buffer| &**buffer));
         } else {
-            self.ctx.send_renderer_message(CanvasMsg::WebGL(WebGLCommand::BindVertexArray(None)));
+            self.ctx.send_command(WebGLCommand::BindVertexArray(None));
             self.bound_vao.set(None);
             self.ctx.set_bound_attrib_buffers(iter::empty());
         }
     }
 }
 
 impl WebGLExtension for OESVertexArrayObject {
     type Extension = OESVertexArrayObject;
--- a/servo/components/script/dom/webgl_extensions/ext/webglvertexarrayobjectoes.rs
+++ b/servo/components/script/dom/webgl_extensions/ext/webglvertexarrayobjectoes.rs
@@ -1,26 +1,26 @@
 /* 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::webgl::WebGLVertexArrayId;
 use core::cell::Ref;
 use core::iter::FromIterator;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WebGLVertexArrayObjectOESBinding;
 use dom::bindings::js::{JS, MutNullableJS};
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::globalscope::GlobalScope;
 use dom::webglbuffer::WebGLBuffer;
 use dom::webglobject::WebGLObject;
 use dom_struct::dom_struct;
 use std::cell::Cell;
 use std::collections::HashMap;
-use webrender_api::WebGLVertexArrayId;
 
 #[dom_struct]
 pub struct WebGLVertexArrayObjectOES {
     webgl_object_: WebGLObject,
     id: WebGLVertexArrayId,
     ever_bound: Cell<bool>,
     is_deleted: Cell<bool>,
     bound_attrib_buffers: DOMRefCell<HashMap<u32, JS<WebGLBuffer>>>,
--- a/servo/components/script/dom/webgl_extensions/extensions.rs
+++ b/servo/components/script/dom/webgl_extensions/extensions.rs
@@ -1,12 +1,13 @@
 /* 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::webgl::WebGLError;
 use core::iter::FromIterator;
 use core::nonzero::NonZero;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::OESTextureHalfFloatBinding::OESTextureHalfFloatConstants;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::js::Root;
 use dom::bindings::trace::JSTraceable;
 use dom::webglrenderingcontext::WebGLRenderingContext;
@@ -14,17 +15,16 @@ use gleam::gl::GLenum;
 use heapsize::HeapSizeOf;
 use js::jsapi::{JSContext, JSObject};
 use js::jsval::JSVal;
 use ref_filter_map::ref_filter_map;
 use std::cell::Ref;
 use std::collections::{HashMap, HashSet};
 use super::{ext, WebGLExtension};
 use super::wrapper::{WebGLExtensionWrapper, TypedWebGLExtensionWrapper};
-use webrender_api::WebGLError;
 
 // Data types that are implemented for texImage2D and texSubImage2D in WebGLRenderingContext
 // but must trigger a InvalidValue error until the related WebGL Extensions are enabled.
 // Example: https://www.khronos.org/registry/webgl/extensions/OES_texture_float/
 const DEFAULT_DISABLED_TEX_TYPES: [GLenum; 2] = [
     constants::FLOAT, OESTextureHalfFloatConstants::HALF_FLOAT_OES
 ];
 
--- a/servo/components/script/dom/webgl_validations/tex_image_2d.rs
+++ b/servo/components/script/dom/webgl_validations/tex_image_2d.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::webgl::WebGLError::*;
 use dom::bindings::js::Root;
 use dom::webglrenderingcontext::WebGLRenderingContext;
 use dom::webgltexture::WebGLTexture;
 use std::{self, fmt};
 use super::WebGLValidator;
 use super::types::{TexImageTarget, TexDataType, TexFormat};
-use webrender_api::WebGLError::*;
 
 /// The errors that the texImage* family of functions can generate.
 #[derive(Debug)]
 pub enum TexImageValidationError {
     /// An invalid texture target was passed, it contains the invalid target.
     InvalidTextureTarget(u32),
     /// The passed texture target was not bound.
     TextureTargetNotBound(u32),
--- a/servo/components/script/dom/webglbuffer.rs
+++ b/servo/components/script/dom/webglbuffer.rs
@@ -1,69 +1,68 @@
 /* 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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
-use canvas_traits::CanvasMsg;
+use canvas_traits::webgl::{WebGLBufferId, WebGLCommand, WebGLError, WebGLMsgSender, WebGLResult, WebGLVertexArrayId};
+use canvas_traits::webgl::webgl_channel;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WebGLBufferBinding;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::webglobject::WebGLObject;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
 use std::cell::Cell;
 use std::collections::HashSet;
-use webrender_api;
-use webrender_api::{WebGLBufferId, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId};
+
 
 #[dom_struct]
 pub struct WebGLBuffer {
     webgl_object: WebGLObject,
     id: WebGLBufferId,
     /// The target to which this buffer was bound the first time
     target: Cell<Option<u32>>,
     capacity: Cell<usize>,
     is_deleted: Cell<bool>,
     // The Vertex Array Objects that are referencing this buffer
     vao_references: DOMRefCell<Option<HashSet<WebGLVertexArrayId>>>,
     pending_delete: Cell<bool>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
-    renderer: IpcSender<CanvasMsg>,
+    renderer: WebGLMsgSender,
 }
 
 impl WebGLBuffer {
-    fn new_inherited(renderer: IpcSender<CanvasMsg>,
+    fn new_inherited(renderer: WebGLMsgSender,
                      id: WebGLBufferId)
                      -> WebGLBuffer {
         WebGLBuffer {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             target: Cell::new(None),
             capacity: Cell::new(0),
             is_deleted: Cell::new(false),
             vao_references: DOMRefCell::new(None),
             pending_delete: Cell::new(false),
             renderer: renderer,
         }
     }
 
-    pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>)
+    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                      -> Option<Root<WebGLBuffer>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateBuffer(sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        renderer.send(WebGLCommand::CreateBuffer(sender)).unwrap();
 
         let result = receiver.recv().unwrap();
         result.map(|buffer_id| WebGLBuffer::new(window, renderer, buffer_id))
     }
 
     pub fn new(window: &Window,
-               renderer: IpcSender<CanvasMsg>,
+               renderer: WebGLMsgSender,
                id: WebGLBufferId)
               -> Root<WebGLBuffer> {
         reflect_dom_object(box WebGLBuffer::new_inherited(renderer, id),
                            window, WebGLBufferBinding::Wrap)
     }
 }
 
 
@@ -76,44 +75,42 @@ impl WebGLBuffer {
     pub fn bind(&self, target: u32) -> WebGLResult<()> {
         if let Some(previous_target) = self.target.get() {
             if target != previous_target {
                 return Err(WebGLError::InvalidOperation);
             }
         } else {
             self.target.set(Some(target));
         }
-        let msg = CanvasMsg::WebGL(WebGLCommand::BindBuffer(target, Some(self.id)));
+        let msg = WebGLCommand::BindBuffer(target, Some(self.id));
         self.renderer.send(msg).unwrap();
 
         Ok(())
     }
 
     pub fn buffer_data(&self, target: u32, data: &[u8], usage: u32) -> WebGLResult<()> {
         if let Some(previous_target) = self.target.get() {
             if target != previous_target {
                 return Err(WebGLError::InvalidOperation);
             }
         }
         self.capacity.set(data.len());
-        self.renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::BufferData(target, data.to_vec(), usage)))
-            .unwrap();
+        self.renderer.send(WebGLCommand::BufferData(target, data.to_vec(), usage)).unwrap();
 
         Ok(())
     }
 
     pub fn capacity(&self) -> usize {
         self.capacity.get()
     }
 
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
-            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteBuffer(self.id)));
+            let _ = self.renderer.send(WebGLCommand::DeleteBuffer(self.id));
         }
     }
 
     pub fn is_deleted(&self) -> bool {
         self.is_deleted.get()
     }
 
     pub fn target(&self) -> Option<u32> {
@@ -139,17 +136,17 @@ impl WebGLBuffer {
         map.insert(id);
         *vao_refs = Some(map);
     }
 
     pub fn remove_vao_reference(&self, id: WebGLVertexArrayId) {
         if let Some(ref mut vao_refs) = *self.vao_references.borrow_mut() {
             if vao_refs.take(&id).is_some() && self.pending_delete.get() {
                 // WebGL spec: The deleted buffers should no longer be valid when the VAOs are deleted
-                let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteBuffer(self.id)));
+                let _ = self.renderer.send(WebGLCommand::DeleteBuffer(self.id));
                 self.is_deleted.set(true);
             }
         }
     }
 }
 
 impl Drop for WebGLBuffer {
     fn drop(&mut self) {
--- a/servo/components/script/dom/webglframebuffer.rs
+++ b/servo/components/script/dom/webglframebuffer.rs
@@ -1,28 +1,27 @@
 /* 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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
-use canvas_traits::CanvasMsg;
+use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId};
+use canvas_traits::webgl::{WebGLMsgSender, WebGLResult, WebGLError};
+use canvas_traits::webgl::webgl_channel;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::js::{JS, Root};
 use dom::bindings::reflector::reflect_dom_object;
 use dom::webglobject::WebGLObject;
 use dom::webglrenderbuffer::WebGLRenderbuffer;
 use dom::webgltexture::WebGLTexture;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
 use std::cell::Cell;
-use webrender_api;
-use webrender_api::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};
 
 #[must_root]
 #[derive(JSTraceable, Clone, HeapSizeOf)]
 enum WebGLFramebufferAttachment {
     Renderbuffer(JS<WebGLRenderbuffer>),
     Texture { texture: JS<WebGLTexture>, level: i32 },
 }
 
@@ -31,28 +30,28 @@ pub struct WebGLFramebuffer {
     webgl_object: WebGLObject,
     id: WebGLFramebufferId,
     /// target can only be gl::FRAMEBUFFER at the moment
     target: Cell<Option<u32>>,
     is_deleted: Cell<bool>,
     size: Cell<Option<(i32, i32)>>,
     status: Cell<u32>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
-    renderer: IpcSender<CanvasMsg>,
+    renderer: WebGLMsgSender,
 
     // The attachment points for textures and renderbuffers on this
     // FBO.
     color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
     depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
     stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
     depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
 }
 
 impl WebGLFramebuffer {
-    fn new_inherited(renderer: IpcSender<CanvasMsg>,
+    fn new_inherited(renderer: WebGLMsgSender,
                      id: WebGLFramebufferId)
                      -> WebGLFramebuffer {
         WebGLFramebuffer {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             target: Cell::new(None),
             is_deleted: Cell::new(false),
             renderer: renderer,
@@ -60,27 +59,27 @@ impl WebGLFramebuffer {
             status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
             color: DOMRefCell::new(None),
             depth: DOMRefCell::new(None),
             stencil: DOMRefCell::new(None),
             depthstencil: DOMRefCell::new(None),
         }
     }
 
-    pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>)
+    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                      -> Option<Root<WebGLFramebuffer>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        renderer.send(WebGLCommand::CreateFramebuffer(sender)).unwrap();
 
         let result = receiver.recv().unwrap();
         result.map(|fb_id| WebGLFramebuffer::new(window, renderer, fb_id))
     }
 
     pub fn new(window: &Window,
-               renderer: IpcSender<CanvasMsg>,
+               renderer: WebGLMsgSender,
                id: WebGLFramebufferId)
                -> Root<WebGLFramebuffer> {
         reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                            window,
                            WebGLFramebufferBinding::Wrap)
     }
 }
 
@@ -93,23 +92,23 @@ impl WebGLFramebuffer {
     pub fn bind(&self, target: u32) {
         // Update the framebuffer status on binding.  It may have
         // changed if its attachments were resized or deleted while
         // we've been unbound.
         self.update_status();
 
         self.target.set(Some(target));
         let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
-        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
+        self.renderer.send(cmd).unwrap();
     }
 
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
-            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
+            let _ = self.renderer.send(WebGLCommand::DeleteFramebuffer(self.id));
         }
     }
 
     pub fn is_deleted(&self) -> bool {
         self.is_deleted.get()
     }
 
     pub fn size(&self) -> Option<(i32, i32)> {
@@ -200,20 +199,20 @@ impl WebGLFramebuffer {
             }
 
             _ => {
                 *binding.borrow_mut() = None;
                 None
             }
         };
 
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
-                                                                                  attachment,
-                                                                                  constants::RENDERBUFFER,
-                                                                                  rb_id))).unwrap();
+        self.renderer.send(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
+                                                                 attachment,
+                                                                 constants::RENDERBUFFER,
+                                                                 rb_id)).unwrap();
 
         self.update_status();
         Ok(())
     }
 
     pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                      level: i32) -> WebGLResult<()> {
         let binding = match attachment {
@@ -276,21 +275,21 @@ impl WebGLFramebuffer {
             }
 
             _ => {
                 *binding.borrow_mut() = None;
                 None
             }
         };
 
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
-                                                                               attachment,
-                                                                               textarget,
-                                                                               tex_id,
-                                                                               level))).unwrap();
+        self.renderer.send(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
+                                                              attachment,
+                                                              textarget,
+                                                              tex_id,
+                                                              level)).unwrap();
 
         self.update_status();
         Ok(())
     }
 
     fn with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F)
         where F: FnMut(&DOMRefCell<Option<WebGLFramebufferAttachment>>)
     {
--- a/servo/components/script/dom/webglprogram.rs
+++ b/servo/components/script/dom/webglprogram.rs
@@ -1,70 +1,68 @@
 /* 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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
-use canvas_traits::CanvasMsg;
+use canvas_traits::webgl::{WebGLCommand, WebGLError, WebGLMsgSender, WebGLParameter, WebGLProgramId, WebGLResult};
+use canvas_traits::webgl::webgl_channel;
 use dom::bindings::codegen::Bindings::WebGLProgramBinding;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::webglactiveinfo::WebGLActiveInfo;
 use dom::webglobject::WebGLObject;
 use dom::webglrenderingcontext::MAX_UNIFORM_AND_ATTRIBUTE_LEN;
 use dom::webglshader::WebGLShader;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
 use std::cell::Cell;
-use webrender_api;
-use webrender_api::{WebGLCommand, WebGLError, WebGLParameter, WebGLProgramId, WebGLResult};
 
 #[dom_struct]
 pub struct WebGLProgram {
     webgl_object: WebGLObject,
     id: WebGLProgramId,
     is_deleted: Cell<bool>,
     link_called: Cell<bool>,
     linked: Cell<bool>,
     fragment_shader: MutNullableJS<WebGLShader>,
     vertex_shader: MutNullableJS<WebGLShader>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
-    renderer: IpcSender<CanvasMsg>,
+    renderer: WebGLMsgSender,
 }
 
 impl WebGLProgram {
-    fn new_inherited(renderer: IpcSender<CanvasMsg>,
+    fn new_inherited(renderer: WebGLMsgSender,
                      id: WebGLProgramId)
                      -> WebGLProgram {
         WebGLProgram {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             is_deleted: Cell::new(false),
             link_called: Cell::new(false),
             linked: Cell::new(false),
             fragment_shader: Default::default(),
             vertex_shader: Default::default(),
             renderer: renderer,
         }
     }
 
-    pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>)
+    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                      -> Option<Root<WebGLProgram>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateProgram(sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        renderer.send(WebGLCommand::CreateProgram(sender)).unwrap();
 
         let result = receiver.recv().unwrap();
         result.map(|program_id| WebGLProgram::new(window, renderer, program_id))
     }
 
     pub fn new(window: &Window,
-               renderer: IpcSender<CanvasMsg>,
+               renderer: WebGLMsgSender,
                id: WebGLProgramId)
                -> Root<WebGLProgram> {
         reflect_dom_object(box WebGLProgram::new_inherited(renderer, id),
                            window,
                            WebGLProgramBinding::Wrap)
     }
 }
 
@@ -73,17 +71,17 @@ impl WebGLProgram {
     pub fn id(&self) -> WebGLProgramId {
         self.id
     }
 
     /// glDeleteProgram
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
-            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteProgram(self.id)));
+            let _ = self.renderer.send(WebGLCommand::DeleteProgram(self.id));
 
             if let Some(shader) = self.fragment_shader.get() {
                 shader.decrement_attached_counter();
             }
 
             if let Some(shader) = self.vertex_shader.get() {
                 shader.decrement_attached_counter();
             }
@@ -112,39 +110,39 @@ impl WebGLProgram {
         }
 
         match self.vertex_shader.get() {
             Some(ref shader) if shader.successfully_compiled() => {},
             _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
         }
 
         self.linked.set(true);
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::LinkProgram(self.id))).unwrap();
+        self.renderer.send(WebGLCommand::LinkProgram(self.id)).unwrap();
         Ok(())
     }
 
     /// glUseProgram
     pub fn use_program(&self) -> WebGLResult<()> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidOperation);
         }
         if !self.linked.get() {
             return Err(WebGLError::InvalidOperation);
         }
 
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::UseProgram(self.id))).unwrap();
+        self.renderer.send(WebGLCommand::UseProgram(self.id)).unwrap();
         Ok(())
     }
 
     /// glValidateProgram
     pub fn validate(&self) -> WebGLResult<()> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidOperation);
         }
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::ValidateProgram(self.id))).unwrap();
+        self.renderer.send(WebGLCommand::ValidateProgram(self.id)).unwrap();
         Ok(())
     }
 
     /// glAttachShader
     pub fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
         if self.is_deleted() || shader.is_deleted() {
             return Err(WebGLError::InvalidOperation);
         }
@@ -161,17 +159,17 @@ impl WebGLProgram {
         // shader.
         if shader_slot.get().is_some() {
             return Err(WebGLError::InvalidOperation);
         }
 
         shader_slot.set(Some(shader));
         shader.increment_attached_counter();
 
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::AttachShader(self.id, shader.id()))).unwrap();
+        self.renderer.send(WebGLCommand::AttachShader(self.id, shader.id())).unwrap();
 
         Ok(())
     }
 
     /// glDetachShader
     pub fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidOperation);
@@ -191,17 +189,17 @@ impl WebGLProgram {
             None =>
                 return Err(WebGLError::InvalidOperation),
             _ => {}
         }
 
         shader_slot.set(None);
         shader.decrement_attached_counter();
 
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DetachShader(self.id, shader.id()))).unwrap();
+        self.renderer.send(WebGLCommand::DetachShader(self.id, shader.id())).unwrap();
 
         Ok(())
     }
 
     /// glBindAttribLocation
     pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidOperation);
@@ -211,42 +209,42 @@ impl WebGLProgram {
         }
 
         // Check if the name is reserved
         if name.starts_with("gl_") || name.starts_with("webgl") || name.starts_with("_webgl_") {
             return Err(WebGLError::InvalidOperation);
         }
 
         self.renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::BindAttribLocation(self.id, index, String::from(name))))
+            .send(WebGLCommand::BindAttribLocation(self.id, index, String::from(name)))
             .unwrap();
         Ok(())
     }
 
     pub fn get_active_uniform(&self, index: u32) -> WebGLResult<Root<WebGLActiveInfo>> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidValue);
         }
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
         self.renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetActiveUniform(self.id, index, sender)))
+            .send(WebGLCommand::GetActiveUniform(self.id, index, sender))
             .unwrap();
 
         receiver.recv().unwrap().map(|(size, ty, name)|
             WebGLActiveInfo::new(self.global().as_window(), size, ty, DOMString::from(name)))
     }
 
     /// glGetActiveAttrib
     pub fn get_active_attrib(&self, index: u32) -> WebGLResult<Root<WebGLActiveInfo>> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidValue);
         }
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
         self.renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetActiveAttrib(self.id, index, sender)))
+            .send(WebGLCommand::GetActiveAttrib(self.id, index, sender))
             .unwrap();
 
         receiver.recv().unwrap().map(|(size, ty, name)|
             WebGLActiveInfo::new(self.global().as_window(), size, ty, DOMString::from(name)))
     }
 
     /// glGetAttribLocation
     pub fn get_attrib_location(&self, name: DOMString) -> WebGLResult<Option<i32>> {
@@ -261,19 +259,19 @@ impl WebGLProgram {
         if name.starts_with("gl_") {
             return Err(WebGLError::InvalidOperation);
         }
 
         if name.starts_with("webgl") || name.starts_with("_webgl_") {
             return Ok(None);
         }
 
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
         self.renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetAttribLocation(self.id, String::from(name), sender)))
+            .send(WebGLCommand::GetAttribLocation(self.id, String::from(name), sender))
             .unwrap();
         Ok(receiver.recv().unwrap())
     }
 
     /// glGetUniformLocation
     pub fn get_uniform_location(&self, name: DOMString) -> WebGLResult<Option<i32>> {
         if !self.is_linked() || self.is_deleted() {
             return Err(WebGLError::InvalidOperation);
@@ -282,19 +280,19 @@ impl WebGLProgram {
             return Err(WebGLError::InvalidValue);
         }
 
         // Check if the name is reserved
         if name.starts_with("webgl") || name.starts_with("_webgl_") {
             return Ok(None);
         }
 
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
         self.renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetUniformLocation(self.id, String::from(name), sender)))
+            .send(WebGLCommand::GetUniformLocation(self.id, String::from(name), sender))
             .unwrap();
         Ok(receiver.recv().unwrap())
     }
 
     /// glGetProgramInfoLog
     pub fn get_info_log(&self) -> WebGLResult<String> {
         if self.is_deleted() {
             return Err(WebGLError::InvalidOperation);
@@ -303,25 +301,25 @@ impl WebGLProgram {
             let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
                 (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
                 _ => false
             };
             if !shaders_compiled {
                 return Ok("One or more shaders failed to compile".to_string());
             }
         }
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetProgramInfoLog(self.id, sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.renderer.send(WebGLCommand::GetProgramInfoLog(self.id, sender)).unwrap();
         Ok(receiver.recv().unwrap())
     }
 
     /// glGetProgramParameter
     pub fn parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetProgramParameter(self.id, param_id, sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.renderer.send(WebGLCommand::GetProgramParameter(self.id, param_id, sender)).unwrap();
         receiver.recv().unwrap()
     }
 }
 
 impl Drop for WebGLProgram {
     fn drop(&mut self) {
         self.delete();
     }
--- a/servo/components/script/dom/webglrenderbuffer.rs
+++ b/servo/components/script/dom/webglrenderbuffer.rs
@@ -1,64 +1,61 @@
 /* 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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
-use canvas_traits::CanvasMsg;
+use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLError, WebGLMsgSender, WebGLRenderbufferId, WebGLResult};
 use dom::bindings::codegen::Bindings::WebGLRenderbufferBinding;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::webglobject::WebGLObject;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
 use std::cell::Cell;
-use webrender_api;
-use webrender_api::{WebGLCommand, WebGLRenderbufferId, WebGLResult, WebGLError};
 
 #[dom_struct]
 pub struct WebGLRenderbuffer {
     webgl_object: WebGLObject,
     id: WebGLRenderbufferId,
     ever_bound: Cell<bool>,
     is_deleted: Cell<bool>,
     size: Cell<Option<(i32, i32)>>,
     internal_format: Cell<Option<u32>>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
-    renderer: IpcSender<CanvasMsg>,
+    renderer: WebGLMsgSender,
 }
 
 impl WebGLRenderbuffer {
-    fn new_inherited(renderer: IpcSender<CanvasMsg>,
+    fn new_inherited(renderer: WebGLMsgSender,
                      id: WebGLRenderbufferId)
                      -> WebGLRenderbuffer {
         WebGLRenderbuffer {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             ever_bound: Cell::new(false),
             is_deleted: Cell::new(false),
             renderer: renderer,
             internal_format: Cell::new(None),
             size: Cell::new(None),
         }
     }
 
-    pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>)
+    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                      -> Option<Root<WebGLRenderbuffer>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateRenderbuffer(sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        renderer.send(WebGLCommand::CreateRenderbuffer(sender)).unwrap();
 
         let result = receiver.recv().unwrap();
         result.map(|renderbuffer_id| WebGLRenderbuffer::new(window, renderer, renderbuffer_id))
     }
 
     pub fn new(window: &Window,
-               renderer: IpcSender<CanvasMsg>,
+               renderer: WebGLMsgSender,
                id: WebGLRenderbufferId)
                -> Root<WebGLRenderbuffer> {
         reflect_dom_object(box WebGLRenderbuffer::new_inherited(renderer, id),
                            window,
                            WebGLRenderbufferBinding::Wrap)
     }
 }
 
@@ -69,24 +66,24 @@ impl WebGLRenderbuffer {
     }
 
     pub fn size(&self) -> Option<(i32, i32)> {
         self.size.get()
     }
 
     pub fn bind(&self, target: u32) {
         self.ever_bound.set(true);
-        let msg = CanvasMsg::WebGL(WebGLCommand::BindRenderbuffer(target, Some(self.id)));
+        let msg = WebGLCommand::BindRenderbuffer(target, Some(self.id));
         self.renderer.send(msg).unwrap();
     }
 
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
-            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteRenderbuffer(self.id)));
+            let _ = self.renderer.send(WebGLCommand::DeleteRenderbuffer(self.id));
         }
     }
 
     pub fn is_deleted(&self) -> bool {
         self.is_deleted.get()
     }
 
     pub fn ever_bound(&self) -> bool {
@@ -105,17 +102,16 @@ impl WebGLRenderbuffer {
 
             _ => return Err(WebGLError::InvalidEnum),
         };
 
         // FIXME: Check that w/h are < MAX_RENDERBUFFER_SIZE
 
         // FIXME: Invalidate completeness after the call
 
-        let msg = CanvasMsg::WebGL(WebGLCommand::RenderbufferStorage(constants::RENDERBUFFER,
-                                                                     internal_format, width, height));
+        let msg = WebGLCommand::RenderbufferStorage(constants::RENDERBUFFER, internal_format, width, height);
         self.renderer.send(msg).unwrap();
 
         self.size.set(Some((width, height)));
 
         Ok(())
     }
 }
--- a/servo/components/script/dom/webglrenderingcontext.rs
+++ b/servo/components/script/dom/webglrenderingcontext.rs
@@ -1,30 +1,33 @@
 /* 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 byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
-use canvas_traits::{CanvasCommonMsg, CanvasMsg, byte_swap, multiply_u8_pixel};
+use canvas_traits::canvas::{byte_swap, multiply_u8_pixel};
+use canvas_traits::webgl::{WebGLContextShareMode, WebGLCommand, WebGLError};
+use canvas_traits::webgl::{WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLParameter, WebVRCommand};
+use canvas_traits::webgl::WebGLError::*;
+use canvas_traits::webgl::webgl_channel;
 use core::cell::Ref;
 use core::iter::FromIterator;
 use core::nonzero::NonZero;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
 use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
 use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible};
 use dom::bindings::error::{Error, Fallible};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, LayoutJS, MutNullableJS, Root};
 use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::event::{Event, EventBubbles, EventCancelable};
-use dom::globalscope::GlobalScope;
 use dom::htmlcanvaselement::HTMLCanvasElement;
 use dom::htmlcanvaselement::utils as canvas_utils;
 use dom::node::{Node, NodeDamage, window_from_node};
 use dom::webgl_extensions::WebGLExtensions;
 use dom::webgl_validations::WebGLValidator;
 use dom::webgl_validations::tex_image_2d::{CommonTexImage2DValidator, CommonTexImage2DValidatorResult};
 use dom::webgl_validations::tex_image_2d::{TexImage2DValidator, TexImage2DValidatorResult};
 use dom::webgl_validations::types::{TexDataType, TexFormat, TexImageTarget};
@@ -37,31 +40,28 @@ use dom::webglrenderbuffer::WebGLRenderb
 use dom::webglshader::WebGLShader;
 use dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
 use dom::webgltexture::{TexParameterValue, WebGLTexture};
 use dom::webgluniformlocation::WebGLUniformLocation;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use euclid::Size2D;
 use half::f16;
-use ipc_channel::ipc::{self, IpcSender};
 use js::conversions::ConversionBehavior;
 use js::jsapi::{JSContext, JSObject, Type, Rooted};
 use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
 use js::typedarray::{TypedArray, TypedArrayElement, Float32, Int32};
 use net_traits::image::base::PixelFormat;
 use net_traits::image_cache::ImageResponse;
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
-use script_traits::ScriptMsg;
+use script_layout_interface::HTMLCanvasDataSource;
 use servo_config::prefs::PREFS;
 use std::cell::Cell;
 use std::collections::HashMap;
 use webrender_api;
-use webrender_api::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter};
-use webrender_api::WebGLError::*;
 
 type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>, bool), ()>;
 pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;
 
 macro_rules! handle_potential_webgl_error {
     ($context:ident, $call:expr, $return_on_error:expr) => {
         match $call {
             Ok(ret) => ret,
@@ -87,19 +87,17 @@ macro_rules! handle_potential_webgl_erro
 macro_rules! handle_object_deletion {
     ($self_:expr, $binding:expr, $object:ident, $unbind_command:expr) => {
         if let Some(bound_object) = $binding.get() {
             if bound_object.id() == $object.id() {
                 $binding.set(None);
             }
 
             if let Some(command) = $unbind_command {
-                $self_.ipc_renderer
-                    .send(CanvasMsg::WebGL(command))
-                    .unwrap();
+                $self_.send_command(command);
             }
         }
     };
 }
 
 macro_rules! object_binding_to_js_or_null {
     ($cx: expr, $binding:expr) => {
         {
@@ -130,22 +128,25 @@ bitflags! {
         const PREMULTIPLY_ALPHA = 0x02,
         const CONVERT_COLORSPACE = 0x04,
     }
 }
 
 #[dom_struct]
 pub struct WebGLRenderingContext {
     reflector_: Reflector,
-    #[ignore_heap_size_of = "Defined in ipc-channel"]
-    ipc_renderer: IpcSender<CanvasMsg>,
+    #[ignore_heap_size_of = "Channels are hard"]
+    webgl_sender: WebGLMsgSender,
+    #[ignore_heap_size_of = "Defined in webrender"]
+    webrender_image: Cell<Option<webrender_api::ImageKey>>,
+    share_mode: WebGLContextShareMode,
     #[ignore_heap_size_of = "Defined in offscreen_gl_context"]
     limits: GLLimits,
     canvas: JS<HTMLCanvasElement>,
-    #[ignore_heap_size_of = "Defined in webrender_api"]
+    #[ignore_heap_size_of = "Defined in canvas_traits"]
     last_error: Cell<Option<WebGLError>>,
     texture_unpacking_settings: Cell<TextureUnpacking>,
     texture_unpacking_alignment: Cell<u32>,
     bound_framebuffer: MutNullableJS<WebGLFramebuffer>,
     bound_renderbuffer: MutNullableJS<WebGLRenderbuffer>,
     bound_texture_2d: MutNullableJS<WebGLTexture>,
     bound_texture_cube_map: MutNullableJS<WebGLTexture>,
     bound_buffer_array: MutNullableJS<WebGLBuffer>,
@@ -153,40 +154,42 @@ pub struct WebGLRenderingContext {
     bound_attrib_buffers: DOMRefCell<HashMap<u32, JS<WebGLBuffer>>>,
     current_program: MutNullableJS<WebGLProgram>,
     #[ignore_heap_size_of = "Because it's small"]
     current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>,
     #[ignore_heap_size_of = "Because it's small"]
     current_scissor: Cell<(i32, i32, i32, i32)>,
     #[ignore_heap_size_of = "Because it's small"]
     current_clear_color: Cell<(f32, f32, f32, f32)>,
-    extension_manager: WebGLExtensions
+    extension_manager: WebGLExtensions,
 }
 
 impl WebGLRenderingContext {
     fn new_inherited(window: &Window,
                      canvas: &HTMLCanvasElement,
                      size: Size2D<i32>,
                      attrs: GLContextAttributes)
                      -> Result<WebGLRenderingContext, String> {
         if let Some(true) = PREFS.get("webgl.testing.context_creation_error").as_boolean() {
             return Err("WebGL context creation error forced by pref `webgl.testing.context_creation_error`".into());
         }
 
-        let (sender, receiver) = ipc::channel().unwrap();
-        let script_to_constellation_chan = window.upcast::<GlobalScope>().script_to_constellation_chan();
-        script_to_constellation_chan.send(ScriptMsg::CreateWebGLPaintThread(size, attrs, sender))
-                          .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        let webgl_chan = window.webgl_chan();
+        webgl_chan.send(WebGLMsg::CreateContext(size, attrs, sender))
+                  .unwrap();
         let result = receiver.recv().unwrap();
 
-        result.map(|(ipc_renderer, context_limits)| {
+        result.map(|ctx_data| {
             WebGLRenderingContext {
                 reflector_: Reflector::new(),
-                ipc_renderer: ipc_renderer,
-                limits: context_limits,
+                webgl_sender: ctx_data.sender,
+                webrender_image: Cell::new(None),
+                share_mode: ctx_data.share_mode,
+                limits: ctx_data.limits,
                 canvas: JS::from_ref(canvas),
                 last_error: Cell::new(None),
                 texture_unpacking_settings: Cell::new(CONVERT_COLORSPACE),
                 texture_unpacking_alignment: Cell::new(4),
                 bound_framebuffer: MutNullableJS::new(None),
                 bound_texture_2d: MutNullableJS::new(None),
                 bound_texture_cube_map: MutNullableJS::new(None),
                 bound_buffer_array: MutNullableJS::new(None),
@@ -248,41 +251,53 @@ impl WebGLRenderingContext {
         self.bound_buffer_element_array.get()
     }
 
     pub fn set_bound_buffer_element_array(&self, buffer: Option<&WebGLBuffer>) {
         self.bound_buffer_element_array.set(buffer);
     }
 
     pub fn recreate(&self, size: Size2D<i32>) {
-        self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.webgl_sender.send_resize(size, sender).unwrap();
+
+        if let Err(msg) = receiver.recv().unwrap() {
+            error!("Error resizing WebGLContext: {}", msg);
+            return;
+        };
+
+        // Reset webrender_image because resize creates a new image_key.
+        // The new image key is set in the next handle_layout() method.
+        self.webrender_image.set(None);
 
         // ClearColor needs to be restored because after a resize the GLContext is recreated
         // and the framebuffer is cleared using the default black transparent color.
         let color = self.current_clear_color.get();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3)))
-            .unwrap();
+        self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
 
         // WebGL Spec: Scissor rect must not change if the canvas is resized.
         // See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html
         // NativeContext handling library changes the scissor after a resize, so we need to reset the
         // default scissor when the canvas was created or the last scissor that the user set.
         let rect = self.current_scissor.get();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3)))
-            .unwrap()
+        self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
+    }
+
+    pub fn webgl_sender(&self) -> WebGLMsgSender {
+        self.webgl_sender.clone()
     }
 
-    pub fn ipc_renderer(&self) -> IpcSender<CanvasMsg> {
-        self.ipc_renderer.clone()
+    #[inline]
+    pub fn send_command(&self, command: WebGLCommand) {
+        self.webgl_sender.send(command).unwrap();
     }
 
-    pub fn send_renderer_message(&self, msg: CanvasMsg) {
-        self.ipc_renderer.send(msg).unwrap();
+    #[inline]
+    pub fn send_vr_command(&self, command: WebVRCommand) {
+        self.webgl_sender.send_vr(command).unwrap();
     }
 
     pub fn get_extension_manager<'a>(&'a self) -> &'a WebGLExtensions {
         &self.extension_manager
     }
 
     pub fn webgl_error(&self, err: WebGLError) {
         // TODO(emilio): Add useful debug messages to this
@@ -365,19 +380,17 @@ impl WebGLRenderingContext {
         if indx > self.limits.max_vertex_attribs {
             return self.webgl_error(InvalidValue);
         }
 
         if indx == 0 {
             self.current_vertex_attrib_0.set((x, y, z, w))
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::VertexAttrib(indx, x, y, z, w)))
-            .unwrap();
+        self.send_command(WebGLCommand::VertexAttrib(indx, x, y, z, w));
     }
 
     fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> {
         match self.bound_framebuffer.get() {
             Some(fb) => return fb.size(),
 
             // The window system framebuffer is bound
             None => return Some((self.DrawingBufferWidth(),
@@ -700,18 +713,18 @@ impl WebGLRenderingContext {
 
                 (data, size, false)
             },
             // 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) => {
                 if let Some((mut data, size)) = canvas.fetch_all_data() {
+                    // Pixels got from Canvas have already alpha premultiplied
                     byte_swap(&mut data);
-                    // Pixels got from Canvas have already alpha premultiplied
                     (data, size, true)
                 } else {
                     return Err(());
                 }
             },
             ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video)
                 => unimplemented!(),
         };
@@ -940,35 +953,31 @@ impl WebGLRenderingContext {
                                                                level,
                                                                Some(data_type)));
 
         // Set the unpack alignment.  For textures coming from arrays,
         // this will be the current value of the context's
         // GL_UNPACK_ALIGNMENT, while for textures from images or
         // canvas (produced by rgba8_image_to_tex_image_data()), it
         // will be 1.
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32)))
-            .unwrap();
+        self.send_command(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32));
 
         let format = internal_format.as_gl_constant();
         let data_type = data_type.as_gl_constant();
         let internal_format = self.extension_manager.get_effective_tex_internal_format(format, data_type);
 
         // TODO(emilio): convert colorspace if requested
         let msg = WebGLCommand::TexImage2D(target.as_gl_constant(), level as i32,
                                            internal_format as i32,
                                            width as i32, height as i32,
                                            format,
                                            data_type,
                                            pixels);
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(msg))
-            .unwrap();
+        self.send_command(msg);
 
         if let Some(fb) = self.bound_framebuffer.get() {
             fb.invalidate_texture(&*texture);
         }
     }
 
     fn tex_sub_image_2d(&self,
                         texture: Root<WebGLTexture>,
@@ -1000,57 +1009,74 @@ impl WebGLRenderingContext {
             return self.webgl_error(InvalidOperation);
         }
 
         // Set the unpack alignment.  For textures coming from arrays,
         // this will be the current value of the context's
         // GL_UNPACK_ALIGNMENT, while for textures from images or
         // canvas (produced by rgba8_image_to_tex_image_data()), it
         // will be 1.
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32)))
-            .unwrap();
+        self.send_command(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32));
 
         // TODO(emilio): convert colorspace if requested
         let msg = WebGLCommand::TexSubImage2D(target.as_gl_constant(),
                                               level as i32, xoffset, yoffset,
                                               width as i32, height as i32,
                                               format.as_gl_constant(),
                                               data_type.as_gl_constant(), pixels);
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(msg))
-            .unwrap()
+        self.send_command(msg);
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14
     fn validate_feature_enum(&self, cap: u32) -> bool {
         match cap {
             constants::BLEND | constants::CULL_FACE | constants::DEPTH_TEST | constants::DITHER |
             constants::POLYGON_OFFSET_FILL | constants::SAMPLE_ALPHA_TO_COVERAGE | constants::SAMPLE_COVERAGE |
             constants::SAMPLE_COVERAGE_INVERT | constants::SCISSOR_TEST | constants::STENCIL_TEST => true,
             _ => {
                 self.webgl_error(InvalidEnum);
                 false
             },
         }
     }
 
     fn get_gl_extensions(&self) -> String {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetExtensions(sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::GetExtensions(sender));
         receiver.recv().unwrap()
     }
+
+    fn layout_handle(&self) -> webrender_api::ImageKey {
+        match self.share_mode {
+            WebGLContextShareMode::SharedTexture => {
+                // WR using ExternalTexture requires a single update message.
+                self.webrender_image.get().unwrap_or_else(|| {
+                    let (sender, receiver) = webgl_channel().unwrap();
+                    self.webgl_sender.send_update_wr_image(sender).unwrap();
+                    let image_key = receiver.recv().unwrap();
+                    self.webrender_image.set(Some(image_key));
+
+                    image_key
+                })
+            },
+            WebGLContextShareMode::Readback => {
+                // WR using Readback requires to update WR image every frame
+                // in order to send the new raw pixels.
+                let (sender, receiver) = webgl_channel().unwrap();
+                self.webgl_sender.send_update_wr_image(sender).unwrap();
+                receiver.recv().unwrap()
+            }
+        }
+    }
 }
 
 impl Drop for WebGLRenderingContext {
     fn drop(&mut self) {
-        self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Close)).unwrap();
+        self.webgl_sender.send_remove().unwrap();
     }
 }
 
 // FIXME: After [1] lands and the relevant Servo and codegen PR too, we should
 // convert all our raw JSObject pointers to proper types.
 //
 // [1]: https://github.com/servo/rust-mozjs/pull/304
 #[allow(unsafe_code)]
@@ -1096,55 +1122,46 @@ unsafe fn fallible_array_buffer_view_to_
 impl WebGLRenderingContextMethods for WebGLRenderingContext {
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
     fn Canvas(&self) -> Root<HTMLCanvasElement> {
         Root::from_ref(&*self.canvas)
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
     fn Flush(&self) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::Flush))
-            .unwrap();
+        self.send_command(WebGLCommand::Flush);
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
     fn Finish(&self) {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::Finish(sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::Finish(sender));
         receiver.recv().unwrap()
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
     fn DrawingBufferWidth(&self) -> i32 {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::DrawingBufferWidth(sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::DrawingBufferWidth(sender));
         receiver.recv().unwrap()
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
     fn DrawingBufferHeight(&self) -> i32 {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::DrawingBufferHeight(sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::DrawingBufferHeight(sender));
         receiver.recv().unwrap()
     }
 
     #[allow(unsafe_code)]
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
     unsafe fn GetBufferParameter(&self, _cx: *mut JSContext, target: u32, parameter: u32) -> JSVal {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetBufferParameter(target, parameter, sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::GetBufferParameter(target, parameter, sender));
+
         match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
             WebGLParameter::Int(val) => Int32Value(val),
             WebGLParameter::Bool(_) => panic!("Buffer parameter should not be bool"),
             WebGLParameter::Float(_) => panic!("Buffer parameter should not be float"),
             WebGLParameter::FloatArray(_) => panic!("Buffer parameter should not be float array"),
             WebGLParameter::String(_) => panic!("Buffer parameter should not be string"),
             WebGLParameter::Invalid => NullValue(),
         }
@@ -1200,20 +1217,19 @@ impl WebGLRenderingContextMethods for We
                 },
                 Err(error) => {
                     self.webgl_error(error);
                     return NullValue();
                 }
             }
         }
 
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::GetParameter(parameter, sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::GetParameter(parameter, sender));
+
         match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
             WebGLParameter::Int(val) => Int32Value(val),
             WebGLParameter::Bool(val) => BooleanValue(val),
             WebGLParameter::Float(val) => DoubleValue(val as f64),
             WebGLParameter::FloatArray(_) => panic!("Parameter should not be float array"),
             WebGLParameter::String(val) => {
                 rooted!(in(cx) let mut rval = UndefinedValue());
                 val.to_jsval(cx, rval.handle_mut());
@@ -1238,23 +1254,23 @@ impl WebGLRenderingContextMethods for We
             constants::NO_ERROR
         };
         self.last_error.set(None);
         error_code
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.2
     fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
 
         // If the send does not succeed, assume context lost
-        if let Err(_) = self.ipc_renderer
-                            .send(CanvasMsg::WebGL(WebGLCommand::GetContextAttributes(sender))) {
+        if self.webgl_sender.send(WebGLCommand::GetContextAttributes(sender)).is_err() {
             return None;
         }
+
         let attrs = receiver.recv().unwrap();
 
         Some(WebGLContextAttributes {
             alpha: attrs.alpha,
             antialias: attrs.antialias,
             depth: attrs.depth,
             failIfMajorPerformanceCaveat: false,
             preferLowPowerToHighPerformance: false,
@@ -1280,79 +1296,74 @@ impl WebGLRenderingContextMethods for We
         self.extension_manager.init_once(|| {
             self.get_gl_extensions()
         });
         self.extension_manager.get_or_init_extension(&name, self)
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn ActiveTexture(&self, texture: u32) {
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::ActiveTexture(texture))).unwrap();
+        self.send_command(WebGLCommand::ActiveTexture(texture));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::BlendColor(r, g, b, a))).unwrap();
+        self.send_command(WebGLCommand::BlendColor(r, g, b, a));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn BlendEquation(&self, mode: u32) {
         if mode != constants::FUNC_ADD {
             return self.webgl_error(InvalidEnum);
         }
 
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::BlendEquation(mode))).unwrap();
+        self.send_command(WebGLCommand::BlendEquation(mode));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
         if mode_rgb != constants::FUNC_ADD || mode_alpha != constants::FUNC_ADD {
             return self.webgl_error(InvalidEnum);
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha)))
-            .unwrap();
+        self.send_command(WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
         // From the WebGL 1.0 spec, 6.13: Viewport Depth Range:
         //
         //     A call to blendFunc will generate an INVALID_OPERATION error if one of the two
         //     factors is set to CONSTANT_COLOR or ONE_MINUS_CONSTANT_COLOR and the other to
         //     CONSTANT_ALPHA or ONE_MINUS_CONSTANT_ALPHA.
         if has_invalid_blend_constants(src_factor, dest_factor) {
             return self.webgl_error(InvalidOperation);
         }
         if has_invalid_blend_constants(dest_factor, src_factor) {
             return self.webgl_error(InvalidOperation);
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::BlendFunc(src_factor, dest_factor)))
-            .unwrap();
+        self.send_command(WebGLCommand::BlendFunc(src_factor, dest_factor));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
         // From the WebGL 1.0 spec, 6.13: Viewport Depth Range:
         //
         //     A call to blendFuncSeparate will generate an INVALID_OPERATION error if srcRGB is
         //     set to CONSTANT_COLOR or ONE_MINUS_CONSTANT_COLOR and dstRGB is set to
         //     CONSTANT_ALPHA or ONE_MINUS_CONSTANT_ALPHA or vice versa.
         if has_invalid_blend_constants(src_rgb, dest_rgb) {
             return self.webgl_error(InvalidOperation);
         }
         if has_invalid_blend_constants(dest_rgb, src_rgb) {
             return self.webgl_error(InvalidOperation);
         }
 
-        self.ipc_renderer.send(
-            CanvasMsg::WebGL(WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha))).unwrap();
+        self.send_command(WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn AttachShader(&self, program: Option<&WebGLProgram>, shader: Option<&WebGLShader>) {
         if let Some(program) = program {
             if let Some(shader) = shader {
                 handle_potential_webgl_error!(self, program.attach_shader(shader));
             }
@@ -1388,19 +1399,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, None)))
-                .unwrap()
+            self.send_command(WebGLCommand::BindBuffer(target, None));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
     fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
         if target != constants::FRAMEBUFFER {
             return self.webgl_error(InvalidOperation);
         }
@@ -1415,17 +1424,17 @@ impl WebGLRenderingContextMethods for We
                 return self.webgl_error(InvalidOperation);
             } else {
                 framebuffer.bind(target);
                 self.bound_framebuffer.set(Some(framebuffer));
             }
         } else {
             // Bind the default framebuffer
             let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Default);
-            self.ipc_renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
+            self.send_command(cmd);
             self.bound_framebuffer.set(framebuffer);
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
     fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
         if target != constants::RENDERBUFFER {
             return self.webgl_error(InvalidEnum);
@@ -1437,19 +1446,17 @@ impl WebGLRenderingContextMethods for We
             // returns.  The conformance tests don't cover this case.
             Some(renderbuffer) if !renderbuffer.is_deleted() => {
                 self.bound_renderbuffer.set(Some(renderbuffer));
                 renderbuffer.bind(target);
             }
             _ => {
                 self.bound_renderbuffer.set(None);
                 // Unbind the currently bound renderbuffer
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::BindRenderbuffer(target, None)))
-                    .unwrap()
+                self.send_command(WebGLCommand::BindRenderbuffer(target, None));
             }
         }
     }
 
     // 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,
@@ -1460,19 +1467,17 @@ impl WebGLRenderingContextMethods for We
         if let Some(texture) = texture {
             match texture.bind(target) {
                 Ok(_) => slot.set(Some(texture)),
                 Err(err) => return self.webgl_error(err),
             }
         } else {
             slot.set(None);
             // Unbind the currently bound texture
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::BindTexture(target, None)))
-                .unwrap()
+            self.send_command(WebGLCommand::BindTexture(target, None));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     fn GenerateMipmap(&self, target: u32) {
         let slot = match target {
             constants::TEXTURE_2D => &self.bound_texture_2d,
             constants::TEXTURE_CUBE_MAP => &self.bound_texture_cube_map,
@@ -1580,19 +1585,17 @@ impl WebGLRenderingContextMethods for We
 
         if offset < 0 {
             return Ok(self.webgl_error(InvalidValue));
         }
 
         if (offset as usize) + data_vec.len() > bound_buffer.capacity() {
             return Ok(self.webgl_error(InvalidValue));
         }
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::BufferSubData(target, offset as isize, data_vec)))
-            .unwrap();
+        self.send_command(WebGLCommand::BufferSubData(target, offset as isize, data_vec));
 
         Ok(())
     }
 
     #[allow(unsafe_code)]
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     unsafe fn CompressedTexImage2D(&self, cx: *mut JSContext, _target: u32, _level: i32, _internal_format: u32,
                             _width: i32, _height: i32, _border: i32, pixels: *mut JSObject) -> Fallible<()> {
@@ -1665,17 +1668,17 @@ impl WebGLRenderingContextMethods for We
 
         let msg = WebGLCommand::CopyTexImage2D(target.as_gl_constant(),
                                                level as i32,
                                                internal_format.as_gl_constant(),
                                                x, y,
                                                width as i32, height as i32,
                                                border as i32);
 
-        self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap()
+        self.send_command(msg);
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32,
                          x: i32, y: i32, width: i32, height: i32) {
         if !self.validate_framebuffer_complete() {
             return;
         }
@@ -1709,178 +1712,156 @@ impl WebGLRenderingContextMethods for We
                 return;
         }
 
         let msg = WebGLCommand::CopyTexSubImage2D(target.as_gl_constant(),
                                                   level as i32, xoffset, yoffset,
                                                   x, y,
                                                   width as i32, height as i32);
 
-        self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap();
+        self.send_command(msg);
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
     fn Clear(&self, mask: u32) {
         if !self.validate_framebuffer_complete() {
             return;
         }
 
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::Clear(mask))).unwrap();
+        self.send_command(WebGLCommand::Clear(mask));
         self.mark_as_dirty();
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
         self.current_clear_color.set((red, green, blue, alpha));
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::ClearColor(red, green, blue, alpha)))
-            .unwrap()
+        self.send_command(WebGLCommand::ClearColor(red, green, blue, alpha));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn ClearDepth(&self, depth: f32) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::ClearDepth(depth as f64)))
-            .unwrap()
+        self.send_command(WebGLCommand::ClearDepth(depth as f64))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn ClearStencil(&self, stencil: i32) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::ClearStencil(stencil)))
-            .unwrap()
+        self.send_command(WebGLCommand::ClearStencil(stencil))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::ColorMask(r, g, b, a)))
-            .unwrap()
+        self.send_command(WebGLCommand::ColorMask(r, g, b, a))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn CullFace(&self, mode: u32) {
         match mode {
             constants::FRONT | constants::BACK | constants::FRONT_AND_BACK =>
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::CullFace(mode)))
-                    .unwrap(),
+                self.send_command(WebGLCommand::CullFace(mode)),
             _ => self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn FrontFace(&self, mode: u32) {
         match mode {
             constants::CW | constants::CCW =>
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::FrontFace(mode)))
-                    .unwrap(),
+                self.send_command(WebGLCommand::FrontFace(mode)),
             _ => self.webgl_error(InvalidEnum),
         }
     }
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn DepthFunc(&self, func: u32) {
         match func {
             constants::NEVER | constants::LESS |
             constants::EQUAL | constants::LEQUAL |
             constants::GREATER | constants::NOTEQUAL |
             constants::GEQUAL | constants::ALWAYS =>
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::DepthFunc(func)))
-                    .unwrap(),
+                self.send_command(WebGLCommand::DepthFunc(func)),
             _ => self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn DepthMask(&self, flag: bool) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::DepthMask(flag)))
-            .unwrap()
+        self.send_command(WebGLCommand::DepthMask(flag))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn DepthRange(&self, near: f32, far: f32) {
         // From the WebGL 1.0 spec, 6.12: Viewport Depth Range:
         //
         //     "A call to depthRange will generate an
         //      INVALID_OPERATION error if zNear is greater than
         //      zFar."
         if near > far {
             return self.webgl_error(InvalidOperation);
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::DepthRange(near as f64, far as f64)))
-            .unwrap()
+        self.send_command(WebGLCommand::DepthRange(near as f64, far as f64))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn Enable(&self, cap: u32) {
         if self.validate_feature_enum(cap) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Enable(cap)))
-                .unwrap();
+            self.send_command(WebGLCommand::Enable(cap));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn Disable(&self, cap: u32) {
         if self.validate_feature_enum(cap) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Disable(cap)))
-                .unwrap()
+            self.send_command(WebGLCommand::Disable(cap));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn CompileShader(&self, shader: Option<&WebGLShader>) {
         if let Some(shader) = shader {
             shader.compile()
         }
     }
 
     // TODO(emilio): Probably in the future we should keep track of the
     // generated objects, either here or in the webgl thread
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
     fn CreateBuffer(&self) -> Option<Root<WebGLBuffer>> {
-        WebGLBuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+        WebGLBuffer::maybe_new(self.global().as_window(), self.webgl_sender.clone())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
     fn CreateFramebuffer(&self) -> Option<Root<WebGLFramebuffer>> {
-        WebGLFramebuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+        WebGLFramebuffer::maybe_new(self.global().as_window(), self.webgl_sender.clone())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
     fn CreateRenderbuffer(&self) -> Option<Root<WebGLRenderbuffer>> {
-        WebGLRenderbuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+        WebGLRenderbuffer::maybe_new(self.global().as_window(), self.webgl_sender.clone())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     fn CreateTexture(&self) -> Option<Root<WebGLTexture>> {
-        WebGLTexture::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+        WebGLTexture::maybe_new(self.global().as_window(), self.webgl_sender.clone())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn CreateProgram(&self) -> Option<Root<WebGLProgram>> {
-        WebGLProgram::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+        WebGLProgram::maybe_new(self.global().as_window(), self.webgl_sender.clone())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn CreateShader(&self, shader_type: u32) -> Option<Root<WebGLShader>> {
         match shader_type {
             constants::VERTEX_SHADER | constants::FRAGMENT_SHADER => {},
             _ => {
                 self.webgl_error(InvalidEnum);
                 return None;
             }
         }
-        WebGLShader::maybe_new(self.global().as_window(), self.ipc_renderer.clone(), shader_type)
+        WebGLShader::maybe_new(self.global().as_window(), self.webgl_sender.clone(), shader_type)
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
     fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
         if let Some(buffer) = buffer {
             if buffer.is_attached_to_vao() {
                 // WebGL spec: The buffers attached to VAOs should still not be deleted.
                 // They are deleted after the VAO is deleted.
@@ -1994,19 +1975,17 @@ impl WebGLRenderingContextMethods for We
                 if first < 0 || count < 0 {
                     return self.webgl_error(InvalidValue);
                 }
 
                 if !self.validate_framebuffer_complete() {
                     return;
                 }
 
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::DrawArrays(mode, first, count)))
-                    .unwrap();
+                self.send_command(WebGLCommand::DrawArrays(mode, first, count));
                 self.mark_as_dirty();
             },
             _ => self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
     fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
@@ -2060,45 +2039,39 @@ impl WebGLRenderingContextMethods for We
             return;
         }
 
         match mode {
             constants::POINTS | constants::LINE_STRIP |
             constants::LINE_LOOP | constants::LINES |
             constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN |
             constants::TRIANGLES => {
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::DrawElements(mode, count, type_, offset)))
-                    .unwrap();
+                self.send_command(WebGLCommand::DrawElements(mode, count, type_, offset));
                 self.mark_as_dirty();
             },
             _ => self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn EnableVertexAttribArray(&self, attrib_id: u32) {
         if attrib_id > self.limits.max_vertex_attribs {
             return self.webgl_error(InvalidValue);
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::EnableVertexAttribArray(attrib_id)))
-            .unwrap()
+        self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn DisableVertexAttribArray(&self, attrib_id: u32) {
         if attrib_id > self.limits.max_vertex_attribs {
             return self.webgl_error(InvalidValue);
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::DisableVertexAttribArray(attrib_id)))
-            .unwrap()
+        self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn GetActiveUniform(&self, program: Option<&WebGLProgram>, index: u32) -> Option<Root<WebGLActiveInfo>> {
         let program = match program {
             Some(program) => program,
             None => {
                 // Reasons to generate InvalidValue error
@@ -2218,21 +2191,21 @@ impl WebGLRenderingContextMethods for We
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn GetShaderPrecisionFormat(&self,
                                 shader_type: u32,
                                 precision_type: u32)
                                 -> Option<Root<WebGLShaderPrecisionFormat>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetShaderPrecisionFormat(shader_type,
-                                                                                       precision_type,
-                                                                                       sender)))
-                                                                                       .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::GetShaderPrecisionFormat(shader_type,
+                                                                 precision_type,
+                                                                 sender));
+
         match receiver.recv().unwrap() {
             Ok((range_min, range_max, precision)) => {
                 Some(WebGLShaderPrecisionFormat::new(self.global().as_window(), range_min, range_max, precision))
             },
             Err(error) => {
                 self.webgl_error(error);
                 None
             }
@@ -2263,18 +2236,18 @@ impl WebGLRenderingContextMethods for We
         if pname == constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING {
             rooted!(in(cx) let mut jsval = NullValue());
             if let Some(buffer) =  self.bound_attrib_buffers.borrow().get(&index) {
                 buffer.to_jsval(cx, jsval.handle_mut());
             }
             return jsval.get();
         }
 
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetVertexAttrib(index, pname, sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::GetVertexAttrib(index, pname, sender));
 
         match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
             WebGLParameter::Int(val) => Int32Value(val),
             WebGLParameter::Bool(val) => BooleanValue(val),
             WebGLParameter::String(_) => panic!("Vertex attrib should not be string"),
             WebGLParameter::Float(_) => panic!("Vertex attrib should not be float"),
             WebGLParameter::FloatArray(val) => {
                 rooted!(in(cx) let mut result = UndefinedValue());
@@ -2282,18 +2255,18 @@ impl WebGLRenderingContextMethods for We
                 result.get()
             }
             WebGLParameter::Invalid => NullValue(),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetVertexAttribOffset(index, pname, sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::GetVertexAttribOffset(index, pname, sender));
 
         handle_potential_webgl_error!(self, receiver.recv().unwrap(), 0) as i64
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn Hint(&self, target: u32, mode: u32) {
         if target != constants::GENERATE_MIPMAP_HINT {
             return self.webgl_error(InvalidEnum);
@@ -2302,34 +2275,30 @@ impl WebGLRenderingContextMethods for We
         match mode {
             constants::FASTEST |
             constants::NICEST |
             constants::DONT_CARE => (),
 
             _ => return self.webgl_error(InvalidEnum),
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::Hint(target, mode)))
-            .unwrap()
+        self.send_command(WebGLCommand::Hint(target, mode));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
     fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
         buffer.map_or(false, |buf| buf.target().is_some() && !buf.is_deleted())
     }
 
     // TODO: We could write this without IPC, recording the calls to `enable` and `disable`.
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn IsEnabled(&self, cap: u32) -> bool {
         if self.validate_feature_enum(cap) {
-            let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::IsEnabled(cap, sender)))
-                .unwrap();
+            let (sender, receiver) = webgl_channel().unwrap();
+            self.send_command(WebGLCommand::IsEnabled(cap, sender));
             return receiver.recv().unwrap();
         }
 
         false
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
     fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
@@ -2357,19 +2326,17 @@ impl WebGLRenderingContextMethods for We
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn LineWidth(&self, width: f32) {
         if width.is_nan() || width <= 0f32 {
             return self.webgl_error(InvalidValue);
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::LineWidth(width)))
-            .unwrap()
+        self.send_command(WebGLCommand::LineWidth(width))
     }
 
     // NOTE: Usage of this function could affect rendering while we keep using
     //   readback to render to the page.
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn PixelStorei(&self, param_name: u32, param_value: i32) {
         let mut texture_settings = self.texture_unpacking_settings.get();
         match param_name {
@@ -2415,19 +2382,17 @@ impl WebGLRenderingContextMethods for We
                 return;
             },
             _ => return self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn PolygonOffset(&self, factor: f32, units: f32) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::PolygonOffset(factor, units)))
-            .unwrap()
+        self.send_command(WebGLCommand::PolygonOffset(factor, units))
     }
 
     #[allow(unsafe_code)]
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.12
     unsafe fn ReadPixels(&self, cx: *mut JSContext, x: i32, y: i32, width: i32, height: i32,
                   format: u32, pixel_type: u32, pixels: *mut JSObject) -> Fallible<()> {
         if pixels.is_null() {
             return Ok(self.webgl_error(InvalidValue));
@@ -2517,121 +2482,105 @@ impl WebGLRenderingContextMethods for We
                 }
                 if y + height > fb_height {
                     height = fb_height - y;
                 }
             }
             _ => return Ok(self.webgl_error(InvalidOperation)),
         };
 
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender)))
-            .unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.send_command(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender));
 
         let result = receiver.recv().unwrap();
 
         for i in 0..height {
             for j in 0..(width * cpp) {
                 data[(dst_offset + i * stride + j) as usize] =
                     result[(i * width * cpp + j) as usize];
             }
         }
 
         Ok(())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn SampleCoverage(&self, value: f32, invert: bool) {
-        self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::SampleCoverage(value, invert))).unwrap();
+        self.send_command(WebGLCommand::SampleCoverage(value, invert));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4
     fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
         if width < 0 || height < 0 {
             return self.webgl_error(InvalidValue)
         }
 
         self.current_scissor.set((x, y, width, height));
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::Scissor(x, y, width, height)))
-            .unwrap()
+        self.send_command(WebGLCommand::Scissor(x, y, width, height));
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
         match func {
             constants::NEVER | constants::LESS | constants::EQUAL | constants::LEQUAL |
             constants::GREATER | constants::NOTEQUAL | constants::GEQUAL | constants::ALWAYS =>
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::StencilFunc(func, ref_, mask)))
-                    .unwrap(),
+                self.send_command(WebGLCommand::StencilFunc(func, ref_, mask)),
             _ => self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
         match face {
             constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => (),
             _ => return self.webgl_error(InvalidEnum),
         }
 
         match func {
             constants::NEVER | constants::LESS | constants::EQUAL | constants::LEQUAL |
             constants::GREATER | constants::NOTEQUAL | constants::GEQUAL | constants::ALWAYS =>
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::StencilFuncSeparate(face, func, ref_, mask)))
-                    .unwrap(),
+                self.send_command(WebGLCommand::StencilFuncSeparate(face, func, ref_, mask)),
             _ => self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn StencilMask(&self, mask: u32) {
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::StencilMask(mask)))
-            .unwrap()
+        self.send_command(WebGLCommand::StencilMask(mask))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn StencilMaskSeparate(&self, face: u32, mask: u32) {
         match face {
             constants::FRONT | constants::BACK | constants::FRONT_AND_BACK =>
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::StencilMaskSeparate(face, mask)))
-                    .unwrap(),
+                self.send_command(WebGLCommand::StencilMaskSeparate(face, mask)),
             _ => return self.webgl_error(InvalidEnum),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
         if self.validate_stencil_actions(fail) && self.validate_stencil_actions(zfail) &&
            self.validate_stencil_actions(zpass) {
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::StencilOp(fail, zfail, zpass)))
-                    .unwrap()
+                self.send_command(WebGLCommand::StencilOp(fail, zfail, zpass));
         } else {
             self.webgl_error(InvalidEnum)
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
         match face {
             constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => (),
             _ => return self.webgl_error(InvalidEnum),
         }
 
         if self.validate_stencil_actions(fail) && self.validate_stencil_actions(zfail) &&
            self.validate_stencil_actions(zpass) {
-                self.ipc_renderer
-                    .send(CanvasMsg::WebGL(WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass)))
-                    .unwrap()
+                self.send_command(WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass))
         } else {
             self.webgl_error(InvalidEnum)
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn LinkProgram(&self, program: Option<&WebGLProgram>) {
         if let Some(program) = program {
@@ -2653,261 +2602,229 @@ impl WebGLRenderingContextMethods for We
         shader.and_then(|s| s.source())
     }
 
     // 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, UniformSetterType::Float, &[val]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform1f(uniform.unwrap().id(), val)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform1f(uniform.unwrap().id(), val))
         }
     }
 
     // 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, UniformSetterType::Int, &[val]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform1i(uniform.unwrap().id(), val)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform1i(uniform.unwrap().id(), val))
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform1iv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default)?;
 
         if self.validate_uniform_parameters(uniform, UniformSetterType::Int, &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform1iv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform1iv(uniform.unwrap().id(), data_vec))
         }
 
         Ok(())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform1fv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
 
         if self.validate_uniform_parameters(uniform, UniformSetterType::Float, &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform1fv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform1fv(uniform.unwrap().id(), data_vec));
         }
 
         Ok(())
     }
 
     // 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, UniformSetterType::FloatVec2, &[x, y]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform2f(uniform.unwrap().id(), x, y)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform2f(uniform.unwrap().id(), x, y));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform2fv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
 
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::FloatVec2,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform2fv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform2fv(uniform.unwrap().id(), data_vec));
         }
 
         Ok(())
     }
 
     // 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,
                                             UniformSetterType::IntVec2,
                                             &[x, y]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform2i(uniform.unwrap().id(), x, y)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform2i(uniform.unwrap().id(), x, y));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform2iv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default)?;
 
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::IntVec2,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform2iv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform2iv(uniform.unwrap().id(), data_vec));
         }
 
         Ok(())
     }
 
     // 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,
                                             UniformSetterType::FloatVec3,
                                             &[x, y, z]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform3f(uniform.unwrap().id(), x, y, z)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform3f(uniform.unwrap().id(), x, y, z));
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform3fv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
 
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::FloatVec3,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform3fv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform3fv(uniform.unwrap().id(), data_vec))
         }
 
         Ok(())
     }
 
     // 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,
                                             UniformSetterType::IntVec3,
                                             &[x, y, z]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform3i(uniform.unwrap().id(), x, y, z)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform3i(uniform.unwrap().id(), x, y, z))
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform3iv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default)?;
 
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::IntVec3,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform3iv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform3iv(uniform.unwrap().id(), data_vec))
         }
 
         Ok(())
     }
 
     // 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,
                                             UniformSetterType::IntVec4,
                                             &[x, y, z, w]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform4i(uniform.unwrap().id(), x, y, z, w)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform4i(uniform.unwrap().id(), x, y, z, w))
         }
     }
 
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform4iv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default)?;
 
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::IntVec4,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform4iv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform4iv(uniform.unwrap().id(), data_vec))
         }
 
         Ok(())
     }
 
     // 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,
                                             UniformSetterType::FloatVec4,
                                             &[x, y, z, w]) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform4f(uniform.unwrap().id(), x, y, z, w)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform4f(uniform.unwrap().id(), x, y, z, w))
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn Uniform4fv(&self,
                   cx: *mut JSContext,
                   uniform: Option<&WebGLUniformLocation>,
                   data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
 
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::FloatVec4,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::Uniform4fv(uniform.unwrap().id(), data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::Uniform4fv(uniform.unwrap().id(), data_vec))
         }
 
         Ok(())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn UniformMatrix2fv(&self,
@@ -2915,19 +2832,17 @@ impl WebGLRenderingContextMethods for We
                         uniform: Option<&WebGLUniformLocation>,
                         transpose: bool,
                         data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::FloatMat2,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::UniformMatrix2fv(uniform.unwrap().id(), transpose, data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::UniformMatrix2fv(uniform.unwrap().id(), transpose, data_vec));
         }
 
         Ok(())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn UniformMatrix3fv(&self,
@@ -2935,19 +2850,17 @@ impl WebGLRenderingContextMethods for We
                         uniform: Option<&WebGLUniformLocation>,
                         transpose: bool,
                         data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::FloatMat3,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::UniformMatrix3fv(uniform.unwrap().id(), transpose, data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::UniformMatrix3fv(uniform.unwrap().id(), transpose, data_vec));
         }
 
         Ok(())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
     #[allow(unsafe_code)]
     unsafe fn UniformMatrix4fv(&self,
@@ -2955,19 +2868,17 @@ impl WebGLRenderingContextMethods for We
                         uniform: Option<&WebGLUniformLocation>,
                         transpose: bool,
                         data: *mut JSObject) -> Fallible<()> {
         assert!(!data.is_null());
         let data_vec = typed_array_or_sequence_to_vec::<Float32>(cx, data, ())?;
         if self.validate_uniform_parameters(uniform,
                                             UniformSetterType::FloatMat4,
                                             &data_vec) {
-            self.ipc_renderer
-                .send(CanvasMsg::WebGL(WebGLCommand::UniformMatrix4fv(uniform.unwrap().id(), transpose, data_vec)))
-                .unwrap()
+            self.send_command(WebGLCommand::UniformMatrix4fv(uniform.unwrap().id(), transpose, data_vec));
         }
 
         Ok(())
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn UseProgram(&self, program: Option<&WebGLProgram>) {
         if let Some(program) = program {
@@ -3093,30 +3004,27 @@ impl WebGLRenderingContextMethods for We
                 }
             },
             _ => return self.webgl_error(InvalidEnum),
 
         }
 
         self.bound_attrib_buffers.borrow_mut().insert(attrib_id, JS::from_ref(&*buffer_array));
 
-        let msg = CanvasMsg::WebGL(
-                    WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset as u32));
-        self.ipc_renderer.send(msg).unwrap()
+        let msg = WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset as u32);
+        self.send_command(msg);
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4
     fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
         if width < 0 || height < 0 {
             return self.webgl_error(InvalidValue)
         }
 
-        self.ipc_renderer
-            .send(CanvasMsg::WebGL(WebGLCommand::Viewport(x, y, width, height)))
-            .unwrap()
+        self.send_command(WebGLCommand::Viewport(x, y, width, height))
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
     #[allow(unsafe_code)]
     unsafe fn TexImage2D(&self,
                   cx: *mut JSContext,
                   target: u32,
                   level: i32,
@@ -3451,23 +3359,23 @@ impl WebGLRenderingContextMethods for We
             Some(fb) => handle_potential_webgl_error!(self, fb.texture2d(attachment, textarget, texture, level)),
             None => self.webgl_error(InvalidOperation),
         };
     }
 }
 
 pub trait LayoutCanvasWebGLRenderingContextHelpers {
     #[allow(unsafe_code)]
-    unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg>;
+    unsafe fn canvas_data_source(&self) -> HTMLCanvasDataSource;
 }
 
 impl LayoutCanvasWebGLRenderingContextHelpers for LayoutJS<WebGLRenderingContext> {
     #[allow(unsafe_code)]
-    unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
-        (*self.unsafe_get()).ipc_renderer.clone()
+    unsafe fn canvas_data_source(&self) -> HTMLCanvasDataSource {
+        HTMLCanvasDataSource::WebGL((*self.unsafe_get()).layout_handle())
     }
 }
 
 #[derive(Debug, PartialEq)]
 pub enum UniformSetterType {
     Int,
     IntVec2,
     IntVec3,
--- a/servo/components/script/dom/webglshader.rs
+++ b/servo/components/script/dom/webglshader.rs
@@ -1,28 +1,25 @@
 /* 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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
 use angle::hl::{BuiltInResources, Output, ShaderValidator};
-use canvas_traits::CanvasMsg;
+use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLMsgSender, WebGLParameter, WebGLResult, WebGLShaderId};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WebGLShaderBinding;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::bindings::str::DOMString;
 use dom::webglobject::WebGLObject;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
 use std::cell::Cell;
 use std::sync::{ONCE_INIT, Once};
-use webrender_api;
-use webrender_api::{WebGLCommand, WebGLParameter, WebGLResult, WebGLShaderId};
 
 #[derive(Clone, Copy, PartialEq, Debug, JSTraceable, HeapSizeOf)]
 pub enum ShaderCompilationStatus {
     NotCompiled,
     Succeeded,
     Failed,
 }
 
@@ -32,29 +29,29 @@ pub struct WebGLShader {
     id: WebGLShaderId,
     gl_type: u32,
     source: DOMRefCell<Option<DOMString>>,
     info_log: DOMRefCell<Option<String>>,
     is_deleted: Cell<bool>,
     attached_counter: Cell<u32>,
     compilation_status: Cell<ShaderCompilationStatus>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
-    renderer: IpcSender<CanvasMsg>,
+    renderer: WebGLMsgSender,
 }
 
 #[cfg(not(target_os = "android"))]
 const SHADER_OUTPUT_FORMAT: Output = Output::Glsl;
 
 #[cfg(target_os = "android")]
 const SHADER_OUTPUT_FORMAT: Output = Output::Essl;
 
 static GLSLANG_INITIALIZATION: Once = ONCE_INIT;
 
 impl WebGLShader {
-    fn new_inherited(renderer: IpcSender<CanvasMsg>,
+    fn new_inherited(renderer: WebGLMsgSender,
                      id: WebGLShaderId,
                      shader_type: u32)
                      -> WebGLShader {
         GLSLANG_INITIALIZATION.call_once(|| ::angle::hl::initialize().unwrap());
         WebGLShader {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             gl_type: shader_type,
@@ -63,28 +60,28 @@ impl WebGLShader {
             is_deleted: Cell::new(false),
             attached_counter: Cell::new(0),
             compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
             renderer: renderer,
         }
     }
 
     pub fn maybe_new(window: &Window,
-                     renderer: IpcSender<CanvasMsg>,
+                     renderer: WebGLMsgSender,
                      shader_type: u32)
                      -> Option<Root<WebGLShader>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateShader(shader_type, sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        renderer.send(WebGLCommand::CreateShader(shader_type, sender)).unwrap();
 
         let result = receiver.recv().unwrap();
         result.map(|shader_id| WebGLShader::new(window, renderer, shader_id, shader_type))
     }
 
     pub fn new(window: &Window,
-               renderer: IpcSender<CanvasMsg>,
+               renderer: WebGLMsgSender,
                id: WebGLShaderId,
                shader_type: u32)
                -> Root<WebGLShader> {
         reflect_dom_object(box WebGLShader::new_inherited(renderer, id, shader_type),
                            window,
                            WebGLShaderBinding::Wrap)
     }
 }
@@ -113,17 +110,17 @@ impl WebGLShader {
                                                        &params).unwrap();
             match validator.compile_and_translate(&[source]) {
                 Ok(translated_source) => {
                     debug!("Shader translated: {}", translated_source);
                     // NOTE: At this point we should be pretty sure that the compilation in the paint thread
                     // will succeed.
                     // It could be interesting to retrieve the info log from the paint thread though
                     let msg = WebGLCommand::CompileShader(self.id, translated_source);
-                    self.renderer.send(CanvasMsg::WebGL(msg)).unwrap();
+                    self.renderer.send(msg).unwrap();
                     self.compilation_status.set(ShaderCompilationStatus::Succeeded);
                 },
                 Err(error) => {
                     self.compilation_status.set(ShaderCompilationStatus::Failed);
                     debug!("Shader {} compilation failed: {}", self.id, error);
                 },
             }
 
@@ -137,17 +134,17 @@ impl WebGLShader {
     }
 
     /// Mark this shader as deleted (if it wasn't previously)
     /// and delete it as if calling glDeleteShader.
     /// Currently does not check if shader is attached
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
-            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteShader(self.id)));
+            let _ = self.renderer.send(WebGLCommand::DeleteShader(self.id));
         }
     }
 
     pub fn is_deleted(&self) -> bool {
         self.is_deleted.get()
     }
 
     pub fn is_attached(&self) -> bool {
@@ -165,18 +162,18 @@ impl WebGLShader {
 
     /// glGetShaderInfoLog
     pub fn info_log(&self) -> Option<String> {
         self.info_log.borrow().clone()
     }
 
     /// glGetParameter
     pub fn parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetShaderParameter(self.id, param_id, sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        self.renderer.send(WebGLCommand::GetShaderParameter(self.id, param_id, sender)).unwrap();
         receiver.recv().unwrap()
     }
 
     /// Get the shader source
     pub fn source(&self) -> Option<DOMString> {
         self.source.borrow().clone()
     }
 
--- a/servo/components/script/dom/webgltexture.rs
+++ b/servo/components/script/dom/webgltexture.rs
@@ -1,28 +1,26 @@
 /* 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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
-use canvas_traits::CanvasMsg;
+
+use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLError, WebGLMsgSender, WebGLResult, WebGLTextureId};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
 use dom::bindings::codegen::Bindings::WebGLTextureBinding;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::webgl_validations::types::{TexImageTarget, TexFormat, TexDataType};
 use dom::webglobject::WebGLObject;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
 use std::cell::Cell;
 use std::cmp;
-use webrender_api;
-use webrender_api::{WebGLCommand, WebGLError, WebGLResult, WebGLTextureId};
 
 pub enum TexParameterValue {
     Float(f32),
     Int(i32),
 }
 
 const MAX_LEVEL_COUNT: usize = 31;
 const MAX_FACE_COUNT: usize = 6;
@@ -41,48 +39,48 @@ pub struct WebGLTexture {
     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,
     // Store information for min and mag filters
     min_filter: Cell<Option<u32>>,
     mag_filter: Cell<Option<u32>>,
     #[ignore_heap_size_of = "Defined in ipc-channel"]
-    renderer: IpcSender<CanvasMsg>,
+    renderer: WebGLMsgSender,
 }
 
 impl WebGLTexture {
-    fn new_inherited(renderer: IpcSender<CanvasMsg>,
+    fn new_inherited(renderer: WebGLMsgSender,
                      id: WebGLTextureId)
                      -> WebGLTexture {
         WebGLTexture {
             webgl_object: WebGLObject::new_inherited(),
             id: id,
             target: Cell::new(None),
             is_deleted: Cell::new(false),
             face_count: Cell::new(0),
             base_mipmap_level: 0,
             min_filter: Cell::new(None),
             mag_filter: Cell::new(None),
             image_info_array: DOMRefCell::new([ImageInfo::new(); MAX_LEVEL_COUNT * MAX_FACE_COUNT]),
             renderer: renderer,
         }
     }
 
-    pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>)
+    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                      -> Option<Root<WebGLTexture>> {
-        let (sender, receiver) = webrender_api::channel::msg_channel().unwrap();
-        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateTexture(sender))).unwrap();
+        let (sender, receiver) = webgl_channel().unwrap();
+        renderer.send(WebGLCommand::CreateTexture(sender)).unwrap();
 
         let result = receiver.recv().unwrap();
         result.map(|texture_id| WebGLTexture::new(window, renderer, texture_id))
     }
 
     pub fn new(window: &Window,
-               renderer: IpcSender<CanvasMsg>,
+               renderer: WebGLMsgSender,
                id: WebGLTextureId)
                -> Root<WebGLTexture> {
         reflect_dom_object(box WebGLTexture::new_inherited(renderer, id),
                            window,
                            WebGLTextureBinding::Wrap)
     }
 }
 
@@ -108,17 +106,17 @@ impl WebGLTexture {
                 constants::TEXTURE_2D => 1,
                 constants::TEXTURE_CUBE_MAP => 6,
                 _ => return Err(WebGLError::InvalidOperation)
             };
             self.face_count.set(face_count);
             self.target.set(Some(target));
         }
 
-        let msg = CanvasMsg::WebGL(WebGLCommand::BindTexture(target, Some(self.id)));
+        let msg = WebGLCommand::BindTexture(target, Some(self.id));
         self.renderer.send(msg).unwrap();
 
         Ok(())
     }
 
     pub fn initialize(&self,
                       target: TexImageTarget,
                       width: u32,
@@ -163,30 +161,30 @@ impl WebGLTexture {
         if !base_image_info.is_power_of_two() {
             return Err(WebGLError::InvalidOperation);
         }
 
         if base_image_info.is_compressed_format() {
             return Err(WebGLError::InvalidOperation);
         }
 
-        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GenerateMipmap(target))).unwrap();
+        self.renderer.send(WebGLCommand::GenerateMipmap(target)).unwrap();
 
         if self.base_mipmap_level + base_image_info.get_max_mimap_levels() == 0 {
             return Err(WebGLError::InvalidOperation);
         }
 
         let last_level = self.base_mipmap_level + base_image_info.get_max_mimap_levels() - 1;
         self.populate_mip_chain(self.base_mipmap_level, last_level)
     }
 
     pub fn delete(&self) {
         if !self.is_deleted.get() {
             self.is_deleted.set(true);
-            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteTexture(self.id)));
+            let _ = self.renderer.send(WebGLCommand::DeleteTexture(self.id));
         }
     }
 
     pub fn is_deleted(&self) -> bool {
         self.is_deleted.get()
     }
 
     pub fn target(&self) -> Option<u32> {
@@ -211,46 +209,46 @@ impl WebGLTexture {
                     constants::NEAREST |
                     constants::LINEAR |
                     constants::NEAREST_MIPMAP_NEAREST |
                     constants::LINEAR_MIPMAP_NEAREST |
                     constants::NEAREST_MIPMAP_LINEAR |
                     constants::LINEAR_MIPMAP_LINEAR => {
                         self.min_filter.set(Some(int_value as u32));
                         self.renderer
-                            .send(CanvasMsg::WebGL(WebGLCommand::TexParameteri(target, name, int_value)))
+                            .send(WebGLCommand::TexParameteri(target, name, int_value))
                             .unwrap();
                         Ok(())
                     },
 
                     _ => Err(WebGLError::InvalidEnum),
                 }
             },
             constants::TEXTURE_MAG_FILTER => {
                 match int_value as u32 {
                     constants::NEAREST |
                     constants::LINEAR => {
                         self.mag_filter.set(Some(int_value as u32));
                         self.renderer
-                            .send(CanvasMsg::WebGL(WebGLCommand::TexParameteri(target, name, int_value)))
+                            .send(WebGLCommand::TexParameteri(target, name, int_value))
                             .unwrap();
                         Ok(())
                     },
 
                     _ => Err(WebGLError::InvalidEnum),
                 }
             },
             constants::TEXTURE_WRAP_S |
             constants::TEXTURE_WRAP_T => {
                 match int_value as u32 {
                     constants::CLAMP_TO_EDGE |
                     constants::MIRRORED_REPEAT |
                     constants::REPEAT => {
                         self.renderer
-                            .send(CanvasMsg::WebGL(WebGLCommand::TexParameteri(target, name, int_value)))
+                            .send(WebGLCommand::TexParameteri(target, name, int_value))
                             .unwrap();
                         Ok(())
                     },
 
                     _ => Err(WebGLError::InvalidEnum),
                 }
             },
 
--- a/servo/components/script/dom/webgluniformlocation.rs
+++ b/servo/components/script/dom/webgluniformlocation.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/. */
 
 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
+use canvas_traits::webgl::WebGLProgramId;
 use dom::bindings::codegen::Bindings::WebGLUniformLocationBinding;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::{Reflector, reflect_dom_object};
 use dom::window::Window;
 use dom_struct::dom_struct;
-use webrender_api::WebGLProgramId;
 
 #[dom_struct]
 pub struct WebGLUniformLocation {
     reflector_: Reflector,
     id: i32,
     program_id: WebGLProgramId,
 }
 
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -1,15 +1,16 @@
 /* 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 app_units::Au;
 use base64;
 use bluetooth_traits::BluetoothRequest;
+use canvas_traits::webgl::WebGLChan;
 use cssparser::{Parser, ParserInput};
 use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
 use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
 use dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHandlerNonNull;
 use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
 use dom::bindings::codegen::Bindings::FunctionBinding::Function;
@@ -259,17 +260,21 @@ pub struct Window {
 
     /// All the MediaQueryLists we need to update
     media_query_lists: WeakMediaQueryListVec,
 
     test_runner: MutNullableJS<TestRunner>,
 
     /// A handle for communicating messages to the webvr thread, if available.
     #[ignore_heap_size_of = "channels are hard"]
-    webvr_thread: Option<IpcSender<WebVRMsg>>,
+    webgl_chan: WebGLChan,
+
+    /// A handle for communicating messages to the webvr thread, if available.
+    #[ignore_heap_size_of = "channels are hard"]
+    webvr_chan: Option<IpcSender<WebVRMsg>>,
 
     /// A map for storing the previous permission state read results.
     permission_state_invocation_results: DOMRefCell<HashMap<String, PermissionState>>,
 
     /// All of the elements that have an outstanding image request that was
     /// initiated by layout during a reflow. They are stored in the script thread
     /// to ensure that the element can be marked dirty when the image data becomes
     /// available at some point in the future.
@@ -375,18 +380,22 @@ impl Window {
     pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Vector2D<f32>>) {
         *self.scroll_offsets.borrow_mut() = offsets
     }
 
     pub fn current_viewport(&self) -> Rect<Au> {
         self.current_viewport.clone().get()
     }
 
+    pub fn webgl_chan(&self) -> WebGLChan {
+        self.webgl_chan.clone()
+    }
+
     pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> {
-        self.webvr_thread.clone()
+        self.webvr_chan.clone()
     }
 
     fn new_paint_worklet(&self) -> Root<Worklet> {
         debug!("Creating new paint worklet.");
         Worklet::new(self, WorkletGlobalScopeType::Paint)
     }
 
     pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> {
@@ -1795,17 +1804,18 @@ impl Window {
                timer_event_chan: IpcSender<TimerEvent>,
                layout_chan: Sender<Msg>,
                id: PipelineId,
                parent_info: Option<(PipelineId, FrameType)>,
                window_size: Option<WindowSizeData>,
                origin: MutableOrigin,
                navigation_start: u64,
                navigation_start_precise: f64,
-               webvr_thread: Option<IpcSender<WebVRMsg>>)
+               webgl_chan: WebGLChan,
+               webvr_chan: Option<IpcSender<WebVRMsg>>)
                -> Root<Window> {
         let layout_rpc: Box<LayoutRPC + Send> = {
             let (rpc_send, rpc_recv) = channel();
             layout_chan.send(Msg::GetRPC(rpc_send)).unwrap();
             rpc_recv.recv().unwrap()
         };
         let error_reporter = CSSErrorReporter {
             pipelineid: id,
@@ -1861,17 +1871,18 @@ impl Window {
             devtools_marker_sender: DOMRefCell::new(None),
             devtools_markers: DOMRefCell::new(HashSet::new()),
             webdriver_script_chan: DOMRefCell::new(None),
             ignore_further_async_events: Default::default(),
             error_reporter: error_reporter,
             scroll_offsets: DOMRefCell::new(HashMap::new()),
             media_query_lists: WeakMediaQueryListVec::new(),
             test_runner: Default::default(),
-            webvr_thread: webvr_thread,
+            webgl_chan: webgl_chan,
+            webvr_chan: webvr_chan,
             permission_state_invocation_results: DOMRefCell::new(HashMap::new()),
             pending_layout_images: DOMRefCell::new(HashMap::new()),
             unminified_js_dir: DOMRefCell::new(None),
             test_worklet: Default::default(),
             paint_worklet: Default::default(),
         };
 
         unsafe {
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -13,16 +13,17 @@
 //! noting when they pertain to ongoing loads (such as resizes/viewport adjustments). When the
 //! initial response is received for an ongoing load, the second phase starts - the frame tree
 //! entry is created, along with the Window and Document objects, and the appropriate parser
 //! takes over the response body. Once parsing is complete, the document lifecycle for loading
 //! a page runs its course and the script thread returns to processing events in the main event
 //! loop.
 
 use bluetooth_traits::BluetoothRequest;
+use canvas_traits::webgl::WebGLPipeline;
 use devtools;
 use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use devtools_traits::CSSError;
 use document_loader::DocumentLoader;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
 use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
@@ -503,18 +504,21 @@ pub struct ScriptThread {
     microtask_queue: MicrotaskQueue,
 
     /// Microtask Queue for adding support for mutation observer microtasks
     mutation_observer_compound_microtask_queued: Cell<bool>,
 
     /// The unit of related similar-origin browsing contexts' list of MutationObserver objects
     mutation_observers: DOMRefCell<Vec<JS<MutationObserver>>>,
 
+    /// A handle to the webgl thread
+    webgl_chan: WebGLPipeline,
+
     /// A handle to the webvr thread, if available
-    webvr_thread: Option<IpcSender<WebVRMsg>>,
+    webvr_chan: Option<IpcSender<WebVRMsg>>,
 
     /// The worklet thread pool
     worklet_thread_pool: DOMRefCell<Option<Rc<WorkletThreadPool>>>,
 
     /// A list of pipelines containing documents that finished loading all their blocking
     /// resources during a turn of the event loop.
     docs_with_no_blocking_loads: DOMRefCell<HashSet<JS<Document>>>,
 
@@ -876,17 +880,18 @@ impl ScriptThread {
             microtask_queue: MicrotaskQueue::default(),
 
             mutation_observer_compound_microtask_queued: Default::default(),
 
             mutation_observers: Default::default(),
 
             layout_to_constellation_chan: state.layout_to_constellation_chan,
 
-            webvr_thread: state.webvr_thread,
+            webgl_chan: state.webgl_chan,
+            webvr_chan: state.webvr_chan,
 
             worklet_thread_pool: Default::default(),
 
             docs_with_no_blocking_loads: Default::default(),
 
             transitioning_nodes: Default::default(),
 
             custom_element_reaction_stack: CustomElementReactionStack::new(),
@@ -2023,17 +2028,18 @@ impl ScriptThread {
                                  ipc_timer_event_chan,
                                  incomplete.layout_chan,
                                  incomplete.pipeline_id,
                                  incomplete.parent_info,
                                  incomplete.window_size,
                                  origin,
                                  incomplete.navigation_start,
                                  incomplete.navigation_start_precise,
-                                 self.webvr_thread.clone());
+                                 self.webgl_chan.channel(),
+                                 self.webvr_chan.clone());
 
         // Initialize the browsing context for the window.
         let window_proxy = self.local_window_proxy(&window,
                                                    incomplete.browsing_context_id,
                                                    incomplete.top_level_browsing_context_id,
                                                    incomplete.parent_info);
         window.init_window_proxy(&window_proxy);
 
--- a/servo/components/script_layout_interface/lib.rs
+++ b/servo/components/script_layout_interface/lib.rs
@@ -38,17 +38,17 @@ extern crate style;
 extern crate webrender_api;
 
 pub mod message;
 pub mod reporter;
 pub mod rpc;
 pub mod wrapper_traits;
 
 use atomic_refcell::AtomicRefCell;
-use canvas_traits::CanvasMsg;
+use canvas_traits::canvas::CanvasMsg;
 use core::nonzero::NonZero;
 use ipc_channel::ipc::IpcSender;
 use libc::c_void;
 use net_traits::image_cache::PendingImageId;
 use script_traits::UntrustedNodeAddress;
 use servo_url::ServoUrl;
 use std::sync::atomic::AtomicIsize;
 use style::data::ElementData;
@@ -119,18 +119,23 @@ pub enum LayoutElementType {
     HTMLTableColElement,
     HTMLTableElement,
     HTMLTableRowElement,
     HTMLTableSectionElement,
     HTMLTextAreaElement,
     SVGSVGElement,
 }
 
+pub enum HTMLCanvasDataSource {
+    WebGL(webrender_api::ImageKey),
+    Image(Option<IpcSender<CanvasMsg>>)
+}
+
 pub struct HTMLCanvasData {
-    pub ipc_renderer: Option<IpcSender<CanvasMsg>>,
+    pub source: HTMLCanvasDataSource,
     pub width: u32,
     pub height: u32,
 }
 
 pub struct SVGSVGData {
     pub width: u32,
     pub height: u32,
 }
--- a/servo/components/script_traits/Cargo.toml
+++ b/servo/components/script_traits/Cargo.toml
@@ -20,17 +20,16 @@ heapsize = "0.4"
 heapsize_derive = "0.1"
 hyper = "0.10"
 hyper_serde = "0.7"
 ipc-channel = "0.8"
 libc = "0.2"
 metrics = {path = "../metrics"}
 msg = {path = "../msg"}
 net_traits = {path = "../net_traits"}
-offscreen_gl_context = { version = "0.11", features = ["serde"] }
 profile_traits = {path = "../profile_traits"}
 rustc-serialize = "0.3.4"
 serde = "1.0"
 servo_atoms = {path = "../atoms"}
 servo_url = {path = "../url"}
 style_traits = {path = "../style_traits", features = ["servo"]}
 time = "0.1.12"
 url = {version = "1.2", features = ["heap_size"]}
--- a/servo/components/script_traits/lib.rs
+++ b/servo/components/script_traits/lib.rs
@@ -19,31 +19,31 @@ extern crate heapsize;
 #[macro_use]
 extern crate heapsize_derive;
 extern crate hyper;
 extern crate hyper_serde;
 extern crate ipc_channel;
 extern crate libc;
 extern crate msg;
 extern crate net_traits;
-extern crate offscreen_gl_context;
 extern crate profile_traits;
 extern crate rustc_serialize;
 #[macro_use] extern crate serde;
 extern crate servo_atoms;
 extern crate servo_url;
 extern crate style_traits;
 extern crate time;
 extern crate webrender_api;
 extern crate webvr_traits;
 
 mod script_msg;
 pub mod webdriver_msg;
 
 use bluetooth_traits::BluetoothRequest;
+use canvas_traits::webgl::WebGLPipeline;
 use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
 use gfx_traits::Epoch;
 use heapsize::HeapSizeOf;
 use hyper::header::Headers;
 use hyper::method::Method;
 use ipc_channel::{Error as IpcError};
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
@@ -519,18 +519,20 @@ pub struct InitialScriptState {
     /// A channel to the developer tools, if applicable.
     pub devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
     /// Information about the initial window size.
     pub window_size: Option<WindowSizeData>,
     /// The ID of the pipeline namespace for this script thread.
     pub pipeline_namespace_id: PipelineNamespaceId,
     /// A ping will be sent on this channel once the script thread shuts down.
     pub content_process_shutdown_chan: IpcSender<()>,
+    /// A channel to the webgl thread used in this pipeline.
+    pub webgl_chan: WebGLPipeline,
     /// A channel to the webvr thread, if available.
-    pub webvr_thread: Option<IpcSender<WebVRMsg>>
+    pub webvr_chan: Option<IpcSender<WebVRMsg>>
 }
 
 /// This trait allows creating a `ScriptThread` without depending on the `script`
 /// crate.
 pub trait ScriptThreadFactory {
     /// Type of message sent from script to layout.
     type Message;
     /// Create a `ScriptThread`.
@@ -754,18 +756,16 @@ pub enum ConstellationMsg {
     /// Requests that the constellation instruct layout to begin a new tick of the animation.
     TickAnimation(PipelineId, AnimationTickType),
     /// Dispatch a webdriver command
     WebDriverCommand(WebDriverCommandMsg),
     /// Reload a top-level browsing context.
     Reload(TopLevelBrowsingContextId),
     /// A log entry, with the top-level browsing context id and thread name
     LogEntry(Option<TopLevelBrowsingContextId>, Option<String>, LogEntry),
-    /// Set the WebVR thread channel.
-    SetWebVRThread(IpcSender<WebVRMsg>),
     /// Dispatch WebVR events to the subscribed script threads.
     WebVREvents(Vec<PipelineId>, Vec<WebVREvent>),
     /// Create a new top level browsing context.
     NewBrowser(ServoUrl, IpcSender<TopLevelBrowsingContextId>),
     /// Make browser visible.
     SelectBrowser(TopLevelBrowsingContextId),
 }
 
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -7,26 +7,25 @@ use CompositorEvent;
 use DocumentState;
 use IFrameLoadInfo;
 use IFrameLoadInfoWithData;
 use LayoutControlMsg;
 use LoadData;
 use MozBrowserEvent;
 use WorkerGlobalScopeInit;
 use WorkerScriptLoadOrigin;
-use canvas_traits::CanvasMsg;
+use canvas_traits::canvas::CanvasMsg;
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::{Point2D, Size2D, TypedSize2D};
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, TraversalDirection};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use net_traits::CoreResourceMsg;
 use net_traits::request::RequestInit;
 use net_traits::storage_thread::StorageType;
-use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use servo_url::ImmutableOrigin;
 use servo_url::ServoUrl;
 use style_traits::CSSPixel;
 use style_traits::cursor::Cursor;
 use style_traits::viewport::ViewportConstraints;
 use webrender_api::ClipId;
 
 /// Messages from the layout to the constellation.
@@ -73,21 +72,16 @@ pub enum ScriptMsg {
     /// Broadcast a storage event to every same-origin pipeline.
     /// The strings are key, old value and new value.
     BroadcastStorageEvent(StorageType, ServoUrl, Option<String>, Option<String>, Option<String>),
     /// Indicates whether this pipeline is currently running animations.
     ChangeRunningAnimationsState(AnimationState),
     /// Requests that a new 2D canvas thread be created. (This is done in the constellation because
     /// 2D canvases may use the GPU and we don't want to give untrusted content access to the GPU.)
     CreateCanvasPaintThread(Size2D<i32>, IpcSender<IpcSender<CanvasMsg>>),
-    /// Requests that a new WebGL thread be created. (This is done in the constellation because
-    /// WebGL uses the GPU and we don't want to give untrusted content access to the GPU.)
-    CreateWebGLPaintThread(Size2D<i32>,
-                           GLContextAttributes,
-                           IpcSender<Result<(IpcSender<CanvasMsg>, GLLimits), String>>),
     /// Notifies the constellation that this frame has received focus.
     Focus,
     /// Forward an event that was sent to the parent window.
     ForwardEvent(PipelineId, CompositorEvent),
     /// Requests that the constellation retrieve the current contents of the clipboard
     GetClipboardContents(IpcSender<String>),
     /// Get the browsing context id for a given pipeline.
     GetBrowsingContextId(PipelineId, IpcSender<Option<BrowsingContextId>>),
--- a/servo/components/servo/lib.rs
+++ b/servo/components/servo/lib.rs
@@ -63,16 +63,18 @@ fn webdriver(port: u16, constellation: S
     webdriver_server::start_server(port, constellation);
 }
 
 #[cfg(not(feature = "webdriver"))]
 fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }
 
 use bluetooth::BluetoothThreadFactory;
 use bluetooth_traits::BluetoothRequest;
+use canvas::gl_context::GLContextFactory;
+use canvas::webgl_thread::WebGLThreads;
 use compositing::IOCompositor;
 use compositing::compositor_thread::{self, CompositorProxy, CompositorReceiver, InitialCompositorState};
 use compositing::windowing::WindowEvent;
 use compositing::windowing::WindowMethods;
 use constellation::{Constellation, InitialConstellationState, UnprivilegedPipelineContent};
 use constellation::{FromCompositorLogger, FromScriptLogger};
 #[cfg(not(target_os = "windows"))]
 use constellation::content_process_sandbox_profile;
@@ -144,17 +146,17 @@ impl<Window> Servo<Window> where Window:
         });
         let devtools_chan = opts.devtools_port.map(|port| {
             devtools::start_server(port)
         });
 
         let mut resource_path = resources_dir_path().unwrap();
         resource_path.push("shaders");
 
-        let (webrender, webrender_api_sender) = {
+        let (mut webrender, webrender_api_sender) = {
             // TODO(gw): Duplicates device_pixels_per_screen_px from compositor. Tidy up!
             let scale_factor = window.hidpi_factor().get();
             let device_pixel_ratio = match opts.device_pixels_per_px {
                 Some(device_pixels_per_px) => device_pixels_per_px,
                 None => match opts.output_file {
                     Some(_) => 1.0,
                     None => scale_factor,
                 }
@@ -206,17 +208,17 @@ impl<Window> Servo<Window> where Window:
         let (constellation_chan, sw_senders) = create_constellation(opts.user_agent.clone(),
                                                                     opts.config_dir.clone(),
                                                                     compositor_proxy.clone_compositor_proxy(),
                                                                     time_profiler_chan.clone(),
                                                                     mem_profiler_chan.clone(),
                                                                     debugger_chan,
                                                                     devtools_chan,
                                                                     supports_clipboard,
-                                                                    &webrender,
+                                                                    &mut webrender,
                                                                     webrender_document,
                                                                     webrender_api_sender);
 
         // Send the constellation's swmanager sender to service worker manager thread
         script::init_service_workers(sw_senders);
 
         if cfg!(feature = "webdriver") {
             if let Some(port) = opts.webdriver_port {
@@ -283,59 +285,80 @@ fn create_compositor_channel(event_loop_
 fn create_constellation(user_agent: Cow<'static, str>,
                         config_dir: Option<PathBuf>,
                         compositor_proxy: CompositorProxy,
                         time_profiler_chan: time::ProfilerChan,
                         mem_profiler_chan: mem::ProfilerChan,
                         debugger_chan: Option<debugger::Sender>,
                         devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                         supports_clipboard: bool,
-                        webrender: &webrender::Renderer,
+                        webrender: &mut webrender::Renderer,
                         webrender_document: webrender_api::DocumentId,
                         webrender_api_sender: webrender_api::RenderApiSender)
                         -> (Sender<ConstellationMsg>, SWManagerSenders) {
     let bluetooth_thread: IpcSender<BluetoothRequest> = BluetoothThreadFactory::new();
 
     let (public_resource_threads, private_resource_threads) =
         new_resource_threads(user_agent,
                              devtools_chan.clone(),
                              time_profiler_chan.clone(),
                              config_dir);
     let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
                                                  Some(webrender_api_sender.create_api()));
 
     let resource_sender = public_resource_threads.sender();
 
+    let (webvr_chan, webvr_constellation_sender, webvr_compositor) = if PREFS.is_webvr_enabled() {
+        // WebVR initialization
+        let (mut handler, sender) = WebVRCompositorHandler::new();
+        let (webvr_thread, constellation_sender) = WebVRThread::spawn(sender);
+        handler.set_webvr_thread_sender(webvr_thread.clone());
+        (Some(webvr_thread), Some(constellation_sender), Some(handler))
+    } else {
+        (None, None, None)
+    };
+
+    // GLContext factory used to create WebGL Contexts
+    let gl_factory = if opts::get().should_use_osmesa() {
+        GLContextFactory::current_osmesa_handle().unwrap()
+    } else {
+        GLContextFactory::current_native_handle(&compositor_proxy).unwrap()
+    };
+
+    // Initialize WebGL Thread entry point.
+    let (webgl_threads, image_handler) = WebGLThreads::new(gl_factory,
+                                                           webrender_api_sender.clone(),
+                                                           webvr_compositor.map(|c| c as Box<_>));
+    // Set webrender external image handler for WebGL textures
+    webrender.set_external_image_handler(image_handler);
+
     let initial_state = InitialConstellationState {
         compositor_proxy,
         debugger_chan,
         devtools_chan,
         bluetooth_thread,
         font_cache_thread,
         public_resource_threads,
         private_resource_threads,
         time_profiler_chan,
         mem_profiler_chan,
         supports_clipboard,
         webrender_document,
         webrender_api_sender,
+        webgl_threads,
+        webvr_chan,
     };
     let (constellation_chan, from_swmanager_sender) =
         Constellation::<script_layout_interface::message::Msg,
                         layout_thread::LayoutThread,
                         script::script_thread::ScriptThread>::start(initial_state);
 
-    if PREFS.is_webvr_enabled() {
-        // WebVR initialization
-        let (mut handler, sender) = WebVRCompositorHandler::new();
-        let webvr_thread = WebVRThread::spawn(constellation_chan.clone(), sender);
-        handler.set_webvr_thread_sender(webvr_thread.clone());
-
-        webrender.set_vr_compositor_handler(handler);
-        constellation_chan.send(ConstellationMsg::SetWebVRThread(webvr_thread)).unwrap();
+    if let Some(webvr_constellation_sender) = webvr_constellation_sender {
+        // Set constellation channel used by WebVR thread to broadcast events
+        webvr_constellation_sender.send(constellation_chan.clone()).unwrap();
     }
 
     // channels to communicate with Service Worker Manager
     let sw_senders = SWManagerSenders {
         swmanager_sender: from_swmanager_sender,
         resource_sender: resource_sender
     };
 
--- a/servo/components/webvr/Cargo.toml
+++ b/servo/components/webvr/Cargo.toml
@@ -5,15 +5,16 @@ authors = ["The Servo Project Developers
 license = "MPL-2.0"
 publish = false
 
 [lib]
 name = "webvr"
 path = "lib.rs"
 
 [dependencies]
+canvas_traits = {path = "../canvas_traits"}
+euclid = "0.15"
 ipc-channel = "0.8"
 log = "0.3"
 msg = {path = "../msg"}
 script_traits = {path = "../script_traits"}
 servo_config = {path = "../config"}
 webvr_traits = {path = "../webvr_traits" }
-webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
--- a/servo/components/webvr/lib.rs
+++ b/servo/components/webvr/lib.rs
@@ -1,17 +1,18 @@
 /* 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/. */
 
 #![deny(unsafe_code)]
 
+extern crate canvas_traits;
+extern crate euclid;
 extern crate ipc_channel;
 #[macro_use]
 extern crate log;
 extern crate msg;
 extern crate script_traits;
 extern crate servo_config;
-extern crate webrender_api;
 extern crate webvr_traits;
 
 mod webvr_thread;
 pub use webvr_thread::{WebVRThread, WebVRCompositorHandler};
--- a/servo/components/webvr/webvr_thread.rs
+++ b/servo/components/webvr/webvr_thread.rs
@@ -1,23 +1,23 @@
 /* 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::webgl;
+use euclid::Size2D;
 use ipc_channel::ipc;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use msg::constellation_msg::PipelineId;
 use script_traits::ConstellationMsg;
 use servo_config::prefs::PREFS;
 use std::{thread, time};
 use std::collections::{HashMap, HashSet};
 use std::sync::mpsc;
 use std::sync::mpsc::{Receiver, Sender};
-use webrender_api;
-use webrender_api::DeviceIntSize;
 use webvr_traits::{WebVRMsg, WebVRResult};
 use webvr_traits::webvr::*;
 
 /// WebVRThread owns native VRDisplays, handles their life cycle inside Servo and
 /// acts a doorman for untrusted VR requests from DOM Objects. These are the key components
 ///    * WebVRThread::spawn() creates a long living thread that waits for VR Commands from DOM objects
 ///      and handles them in its trusted thread. The back and forth comunication with DOM is implemented
 ///      using IPC-channels. This thread creates the VRServiceManager instance, which handles the life cycle
@@ -29,17 +29,17 @@ use webvr_traits::webvr::*;
 ///      is only created when there is at least one live JavaScript context using the WebVR APIs and shuts down it when
 ///      the tab is closed. A single instance of the thread is used to handle multiple JavaScript contexts.
 ///      Constellation channel is used to notify events to the Script Thread.
 ///    * When the WeVR APIs are used in a tab, it's pipeline_id is registered using the RegisterContext message. When
 ///      the tab is closed, UnregisterContext message is sent. This way the WebVR thread has a list of the pipeline
 ///      ids using the WebVR APIs. These ids are used to implement privacy guidelines defined in the WebVR Spec.
 ///    * When a JavaScript thread gains access to present to a headset, WebVRThread is not used as a intermediary in
 ///      the VRDisplay.requestAnimationFrame loop in order to minimize latency. A direct communication with WebRender
-///      is used instead. See WebVRCompositorHandler and the VRCompositorCommanda for more details.
+///      is used instead. See WebVRCompositorHandler and the WebVRCommands for more details.
 pub struct WebVRThread {
     receiver: IpcReceiver<WebVRMsg>,
     sender: IpcSender<WebVRMsg>,
     service: VRServiceManager,
     contexts: HashSet<PipelineId>,
     constellation_chan: Sender<ConstellationMsg>,
     vr_compositor_chan: WebVRCompositorSender,
     polling_events: bool,
@@ -61,25 +61,27 @@ impl WebVRThread {
             contexts: HashSet::new(),
             constellation_chan: constellation_chan,
             vr_compositor_chan: vr_compositor_chan,
             polling_events: false,
             presenting: HashMap::new()
         }
     }
 
-    pub fn spawn(constellation_chan: Sender<ConstellationMsg>,
-                 vr_compositor_chan: WebVRCompositorSender)
-                 -> IpcSender<WebVRMsg> {
+    pub fn spawn(vr_compositor_chan: WebVRCompositorSender)
+                 -> (IpcSender<WebVRMsg>, Sender<Sender<ConstellationMsg>>) {
         let (sender, receiver) = ipc::channel().unwrap();
+        let (constellation_sender, constellation_receiver) = mpsc::channel();
         let sender_clone = sender.clone();
         thread::Builder::new().name("WebVRThread".into()).spawn(move || {
+            let constellation_chan = constellation_receiver.recv().unwrap();
             WebVRThread::new(receiver, sender_clone, constellation_chan, vr_compositor_chan).start();
         }).expect("Thread spawning failed");
-        sender
+
+        (sender, constellation_sender)
     }
 
     fn start(&mut self) {
         while let Ok(msg) = self.receiver.recv() {
             match msg {
                 WebVRMsg::RegisterContext(context) => {
                     self.handle_register_context(context);
                     self.schedule_poll_events();
@@ -300,17 +302,17 @@ impl WebVRThread {
 /// VRDisplays pointers are guaranteed to be valid memory:
 ///    * VRDisplays are owned by the VRServiceManager which lives in the WebVRThread.
 ///    * WebVRCompositorHandler is stopped automatically when a JS tab is closed or the whole browser is closed.
 ///    * WebVRThread and its VRDisplays are destroyed after all tabs are dropped and the browser is about to exit.
 ///      WebVRThread is closed using the Exit message.
 
 pub struct WebVRCompositor(*mut VRDisplay);
 pub struct WebVRCompositorHandler {
-    compositors: HashMap<webrender_api::VRCompositorId, WebVRCompositor>,
+    compositors: HashMap<webgl::WebVRDeviceId, WebVRCompositor>,
     webvr_thread_receiver: Receiver<Option<WebVRCompositor>>,
     webvr_thread_sender: Option<IpcSender<WebVRMsg>>
 }
 
 #[allow(unsafe_code)]
 unsafe impl Send for WebVRCompositor {}
 
 pub type WebVRCompositorSender = Sender<Option<WebVRCompositor>>;
@@ -323,59 +325,59 @@ impl WebVRCompositorHandler {
             webvr_thread_receiver: receiver,
             webvr_thread_sender: None
         });
 
         (instance, sender)
     }
 }
 
-impl webrender_api::VRCompositorHandler for WebVRCompositorHandler {
+impl webgl::WebVRRenderHandler for WebVRCompositorHandler {
     #[allow(unsafe_code)]
-    fn handle(&mut self, cmd: webrender_api::VRCompositorCommand, texture: Option<(u32, DeviceIntSize)>) {
+    fn handle(&mut self, cmd: webgl::WebVRCommand, texture: Option<(u32, Size2D<i32>)>) {
         match cmd {
-            webrender_api::VRCompositorCommand::Create(compositor_id) => {
+            webgl::WebVRCommand::Create(compositor_id) => {
                 self.create_compositor(compositor_id);
             }
-            webrender_api::VRCompositorCommand::SyncPoses(compositor_id, near, far, sender) => {
+            webgl::WebVRCommand::SyncPoses(compositor_id, near, far, sender) => {
                 if let Some(compositor) = self.compositors.get(&compositor_id) {
                     let pose = unsafe {
                         (*compositor.0).sync_poses();
                         (*compositor.0).synced_frame_data(near, far).to_bytes()
                     };
                     let _ = sender.send(Ok(pose));
                 } else {
                     let _ = sender.send(Err(()));
                 }
             }
-            webrender_api::VRCompositorCommand::SubmitFrame(compositor_id, left_bounds, right_bounds) => {
+            webgl::WebVRCommand::SubmitFrame(compositor_id, left_bounds, right_bounds) => {
                 if let Some(compositor) = self.compositors.get(&compositor_id) {
                     if let Some((texture_id, size)) = texture {
                         let layer = VRLayer {
                             texture_id: texture_id,
                             left_bounds: left_bounds,
                             right_bounds: right_bounds,
                             texture_size: Some((size.width as u32, size.height as u32))
                         };
                         unsafe {
                             (*compositor.0).submit_frame(&layer);
                         }
                     }
                 }
             }
-            webrender_api::VRCompositorCommand::Release(compositor_id) => {
+            webgl::WebVRCommand::Release(compositor_id) => {
                 self.compositors.remove(&compositor_id);
             }
         }
     }
 }
 
 impl WebVRCompositorHandler {
     #[allow(unsafe_code)]
-    fn create_compositor(&mut self, display_id: webrender_api::VRCompositorId) {
+    fn create_compositor(&mut self, display_id: webgl::WebVRDeviceId) {
         let sender = match self.webvr_thread_sender {
             Some(ref s) => s,
             None => return,
         };
 
         sender.send(WebVRMsg::CreateCompositor(display_id as u32)).unwrap();
         let display = self.webvr_thread_receiver.recv().unwrap();