servo: Merge #18118 - Revert "Auto merge of #18114 - emilio:revert-webgl-refactor, r=nox" (from servo:oops-webgl); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 17 Aug 2017 00:26:08 -0500
changeset 375276 bc093920843d7495af6c2f29cfd4dd3380833378
parent 375275 e146d838112cce8990f7da28299dee3628af7949
child 375277 0828f347805fe1d28be42424c9c004e932a00780
push id49004
push userservo-vcs-sync@mozilla.com
push dateThu, 17 Aug 2017 06:30:15 +0000
treeherderautoland@bc093920843d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnox, emilio
bugs18118, 18114
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #18118 - Revert "Auto merge of #18114 - emilio:revert-webgl-refactor, r=nox" (from servo:oops-webgl); r=emilio This reverts commit 4d10d39e8fe841c5fe2ac58da2daaa13c10c140e, reversing changes made to ee94e2b7c0bd327abe8f9545b2a1f792f67a2bdd. Source-Repo: https://github.com/servo/servo Source-Revision: fbabcaf614662a52eace21a64f27802c7eff4304
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)]
@@ -593,17 +593,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>),
@@ -923,24 +922,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.
@@ -1244,17 +1235,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,
@@ -1370,17 +1360,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();