servo: Merge #20447 - NCSU Canvas Rendering Project Initial Steps (from Brody-Eastwood:master); r=jdm
authorBrody Eastwood <baeastwo@ncsu.edu>
Mon, 02 Apr 2018 22:24:34 -0400
changeset 776559 35f5e13e1098d838580b83f63bc3a52547ebd213
parent 776558 eab2985673a56ee09f5da6ae62b43a1dfc7e737d
child 776560 ae4d46495f5e78721e9cd68dc46e11b13592b875
push id104907
push userbmo:ato@sny.no
push dateTue, 03 Apr 2018 10:28:20 +0000
reviewersjdm
milestone61.0a1
servo: Merge #20447 - NCSU Canvas Rendering Project Initial Steps (from Brody-Eastwood:master); r=jdm <!-- Please describe your changes on the following line: --> Implements the initial steps from: https://github.com/servo/servo/wiki/Canvas-rendering-project --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 9cd60c8e78af7b80e7578612ae442de26d1a325b
servo/components/canvas/canvas_paint_thread.rs
servo/components/canvas_traits/canvas.rs
servo/components/constellation/constellation.rs
servo/components/layout/display_list/builder.rs
servo/components/layout/fragment.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/canvasrenderingcontext2d.rs
servo/components/script/dom/htmlcanvaselement.rs
servo/components/script/dom/paintrenderingcontext2d.rs
servo/components/script_layout_interface/lib.rs
servo/components/script_traits/script_msg.rs
servo/tests/html/test_canvas_drawimage_canvas.html
servo/tests/html/test_canvas_drawimage_canvas_timed.html
--- a/servo/components/canvas/canvas_paint_thread.rs
+++ b/servo/components/canvas/canvas_paint_thread.rs
@@ -59,16 +59,17 @@ pub struct CanvasPaintThread<'a> {
     state: CanvasPaintState<'a>,
     saved_states: Vec<CanvasPaintState<'a>>,
     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>,
+    canvas_id: CanvasId,
 }
 
 #[derive(Clone)]
 struct CanvasPaintState<'a> {
     draw_options: DrawOptions,
     fill_style: Pattern,
     stroke_style: Pattern,
     stroke_opts: StrokeOptions<'a>,
@@ -94,50 +95,54 @@ impl<'a> CanvasPaintState<'a> {
             shadow_color: Color::transparent(),
         }
     }
 }
 
 impl<'a> CanvasPaintThread<'a> {
     fn new(size: Size2D<i32>,
            webrender_api_sender: webrender_api::RenderApiSender,
-           antialias: AntialiasMode) -> CanvasPaintThread<'a> {
+           antialias: AntialiasMode,
+           canvas_id: CanvasId) -> CanvasPaintThread<'a> {
         let draw_target = CanvasPaintThread::create(size);
         let path_builder = draw_target.create_path_builder();
         let webrender_api = webrender_api_sender.create_api();
         CanvasPaintThread {
             drawtarget: draw_target,
             path_builder: path_builder,
             state: CanvasPaintState::new(antialias),
             saved_states: vec![],
             webrender_api: webrender_api,
             image_key: None,
             old_image_key: None,
             very_old_image_key: None,
+            canvas_id: canvas_id,
         }
     }
 
     /// Creates a new `CanvasPaintThread` and returns an `IpcSender` to
     /// communicate with it.
     pub fn start(size: Size2D<i32>,
                  webrender_api_sender: webrender_api::RenderApiSender,
-                 antialias: bool)
+                 antialias: bool,
+                 canvas_id: CanvasId)
                  -> IpcSender<CanvasMsg> {
         let (sender, receiver) = ipc::channel::<CanvasMsg>().unwrap();
         let antialias = if antialias {
             AntialiasMode::Default
         } else {
             AntialiasMode::None
         };
         thread::Builder::new().name("CanvasThread".to_owned()).spawn(move || {
-            let mut painter = CanvasPaintThread::new(size, webrender_api_sender, antialias);
+            let mut painter = CanvasPaintThread::new(size, webrender_api_sender, antialias, canvas_id);
             loop {
                 let msg = receiver.recv();
                 match msg.unwrap() {
-                    CanvasMsg::Canvas2d(message) => {
+                    CanvasMsg::Canvas2d(message, canvas_id) => {
+                        assert!(canvas_id == painter.canvas_id);
                         match message {
                             Canvas2dMsg::FillText(text, x, y, max_width) => painter.fill_text(text, x, y, max_width),
                             Canvas2dMsg::FillRect(ref rect) => painter.fill_rect(rect),
                             Canvas2dMsg::StrokeRect(ref rect) => painter.stroke_rect(rect),
                             Canvas2dMsg::ClearRect(ref rect) => painter.clear_rect(rect),
                             Canvas2dMsg::BeginPath => painter.begin_path(),
                             Canvas2dMsg::ClosePath => painter.close_path(),
                             Canvas2dMsg::Fill => painter.fill(),
@@ -160,20 +165,20 @@ impl<'a> CanvasPaintThread<'a> {
                                     source_rect,
                                     smoothing_enabled,
                                 )
                             }
                             Canvas2dMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled) => {
                                 painter.draw_image_self(image_size, dest_rect, source_rect, smoothing_enabled)
                             }
                             Canvas2dMsg::DrawImageInOther(
-                                renderer, image_size, dest_rect, source_rect, smoothing, sender
+                                renderer, other_canvas_id, image_size, dest_rect, source_rect, smoothing, sender
                             ) => {
                                 painter.draw_image_in_other(
-                                    renderer, image_size, dest_rect, source_rect, smoothing, sender)
+                                    renderer, other_canvas_id, image_size, dest_rect, source_rect, smoothing, sender)
                             }
                             Canvas2dMsg::MoveTo(ref point) => painter.move_to(point),
                             Canvas2dMsg::LineTo(ref point) => painter.line_to(point),
                             Canvas2dMsg::Rect(ref rect) => painter.rect(rect),
                             Canvas2dMsg::QuadraticCurveTo(ref cp, ref pt) => {
                                 painter.quadratic_curve_to(cp, pt)
                             }
                             Canvas2dMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => {
@@ -215,32 +220,40 @@ impl<'a> CanvasPaintThread<'a> {
                                 )
                             }
                             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::Close => break,
-                    CanvasMsg::Recreate(size) => painter.recreate(size),
-                    CanvasMsg::FromScript(message) => {
+                    CanvasMsg::Close(canvas_id) =>{
+                        assert!(canvas_id == painter.canvas_id);
+                        break;
+                    },
+                    CanvasMsg::Recreate(size, canvas_id) =>{
+                        assert!(canvas_id == painter.canvas_id);
+                        painter.recreate(size);
+                    },
+                    CanvasMsg::FromScript(message, canvas_id) => {
+                        assert!(canvas_id == painter.canvas_id);
                         match message {
                             FromScriptMsg::SendPixels(chan) => {
                                 painter.send_pixels(chan)
                             }
                         }
-                    }
-                    CanvasMsg::FromLayout(message) => {
+                    },
+                    CanvasMsg::FromLayout(message, canvas_id) => {
+                        assert!(canvas_id == painter.canvas_id);
                         match message {
                             FromLayoutMsg::SendData(chan) => {
                                 painter.send_data(chan)
                             }
                         }
-                    }
+                    },
                 }
             }
         }).expect("Thread spawning failed");
 
         sender
     }
 
     fn save_context_state(&mut self) {
@@ -410,32 +423,35 @@ impl<'a> CanvasPaintThread<'a> {
             write_image(&self.drawtarget, image_data, image_size, dest_rect,
                         smoothing_enabled, self.state.draw_options.composition,
                         self.state.draw_options.alpha);
         }
     }
 
     fn draw_image_in_other(&self,
                            renderer: IpcSender<CanvasMsg>,
+                           other_canvas_id: CanvasId,
                            image_size: Size2D<f64>,
                            dest_rect: Rect<f64>,
                            source_rect: Rect<f64>,
                            smoothing_enabled: bool,
                            sender: IpcSender<()>) {
         let mut image_data = self.read_pixels(source_rect.to_i32(), image_size);
         // TODO: avoid double byte_swap.
         byte_swap(&mut image_data);
 
         let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImage(
             image_data.into(),
             source_rect.size,
             dest_rect,
             source_rect,
             smoothing_enabled,
-        ));
+            ),
+            other_canvas_id,
+        );
         renderer.send(msg).unwrap();
         // We acknowledge to the caller here that the data was sent to the
         // other canvas so that if JS immediately afterwards try to get the
         // pixels of the other one, it won't retrieve the other values.
         sender.send(()).unwrap();
     }
 
     fn move_to(&self, point: &Point2D<AzFloat>) {
--- a/servo/components/canvas_traits/canvas.rs
+++ b/servo/components/canvas_traits/canvas.rs
@@ -11,38 +11,41 @@ use std::str::FromStr;
 use webrender_api;
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum FillRule {
     Nonzero,
     Evenodd,
 }
 
+#[derive(Clone, Deserialize, MallocSizeOf, PartialEq, Serialize)]
+pub struct CanvasId(pub u64);
+
 #[derive(Clone, Deserialize, Serialize)]
 pub enum CanvasMsg {
-    Canvas2d(Canvas2dMsg),
-    FromLayout(FromLayoutMsg),
-    FromScript(FromScriptMsg),
-    Recreate(Size2D<i32>),
-    Close,
+    Canvas2d(Canvas2dMsg, CanvasId),
+    FromLayout(FromLayoutMsg, CanvasId),
+    FromScript(FromScriptMsg, CanvasId),
+    Recreate(Size2D<i32>, CanvasId),
+    Close(CanvasId),
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub struct CanvasImageData {
     pub image_key: webrender_api::ImageKey,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum Canvas2dMsg {
     Arc(Point2D<f32>, f32, f32, f32, bool),
     ArcTo(Point2D<f32>, Point2D<f32>, f32),
     DrawImage(ByteBuf, 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<()>),
+        IpcSender<CanvasMsg>, CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool, IpcSender<()>),
     BeginPath,
     BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>),
     ClearRect(Rect<f32>),
     Clip,
     ClosePath,
     Ellipse(Point2D<f32>, f32, f32, f32, f32, f32, bool),
     Fill,
     FillText(String, f64, f64, Option<f64>),
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -90,16 +90,17 @@
 //! See https://github.com/servo/servo/issues/14704
 
 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_thread::WebGLThreads;
+use canvas_traits::canvas::CanvasId;
 use canvas_traits::canvas::CanvasMsg;
 use clipboard::{ClipboardContext, ClipboardProvider};
 use compositing::SendableFrameTree;
 use compositing::compositor_thread::{CompositorProxy, EmbedderMsg, EmbedderProxy};
 use compositing::compositor_thread::Msg as ToCompositorMsg;
 use debugger;
 use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg};
 use euclid::{Size2D, TypedSize2D, TypedScale};
@@ -324,16 +325,19 @@ pub struct Constellation<Message, LTF, S
     /// 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: Option<WebGLThreads>,
 
     /// A channel through which messages can be sent to the webvr thread.
     webvr_chan: Option<IpcSender<WebVRMsg>>,
+
+    /// An Id for the next canvas to use.
+    canvas_id: CanvasId,
 }
 
 /// State needed to construct a constellation.
 pub struct InitialConstellationState {
     /// A channel through which messages can be sent to the embedder.
     pub embedder_proxy: EmbedderProxy,
 
     /// A channel through which messages can be sent to the compositor.
@@ -621,16 +625,17 @@ impl<Message, LTF, STF> Constellation<Me
                     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)
                 }),
                 webgl_threads: state.webgl_threads,
                 webvr_chan: state.webvr_chan,
+                canvas_id: CanvasId(0),
             };
 
             constellation.run();
         }).expect("Thread spawning failed");
 
         (compositor_sender, swmanager_sender)
     }
 
@@ -2198,21 +2203,22 @@ impl<Message, LTF, STF> Constellation<Me
                 self.handle_send_error(parent_pipeline_id, e);
             }
         }
     }
 
     fn handle_create_canvas_paint_thread_msg(
             &mut self,
             size: &Size2D<i32>,
-            response_sender: IpcSender<IpcSender<CanvasMsg>>) {
+            response_sender: IpcSender<(IpcSender<CanvasMsg>, CanvasId)>) {
+        self.canvas_id.0 += 1;
         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) {
+                                              opts::get().enable_canvas_antialiasing, self.canvas_id.clone());
+        if let Err(e) = response_sender.send((sender, self.canvas_id.clone())) {
             warn!("Create canvas 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 {
--- a/servo/components/layout/display_list/builder.rs
+++ b/servo/components/layout/display_list/builder.rs
@@ -1795,17 +1795,18 @@ impl FragmentDisplayListBuilding for Fra
 
                 let (image_key, format) = match canvas_fragment_info.source {
                     CanvasFragmentSource::WebGL(image_key) => (image_key, PixelFormat::BGRA8),
                     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)))
+                                .send(CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender),
+                                canvas_fragment_info.canvas_id.clone()))
                                 .unwrap();
                             (receiver.recv().unwrap().image_key, PixelFormat::BGRA8)
                         },
                         None => return,
                     },
                 };
 
                 let base = create_base_display_item(state);
--- 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::canvas::CanvasMsg;
+use canvas_traits::canvas::{CanvasMsg, CanvasId};
 use context::{LayoutContext, with_thread_local_font_context};
 use display_list::ToLayout;
 use euclid::{Point2D, Vector2D, Rect, Size2D};
 use floats::ClearType;
 use flow::{GetBaseFlow, ImmutableFlowUtils};
 use flow_ref::FlowRef;
 use gfx;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
@@ -340,16 +340,17 @@ pub enum CanvasFragmentSource {
     Image(Option<Arc<Mutex<IpcSender<CanvasMsg>>>>)
 }
 
 #[derive(Clone)]
 pub struct CanvasFragmentInfo {
     pub source: CanvasFragmentSource,
     pub dom_width: Au,
     pub dom_height: Au,
+    pub canvas_id: CanvasId,
 }
 
 impl CanvasFragmentInfo {
     pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
         let source = match data.source {
             HTMLCanvasDataSource::WebGL(texture_id) => {
                 CanvasFragmentSource::WebGL(texture_id)
             },
@@ -357,16 +358,17 @@ impl CanvasFragmentInfo {
                 CanvasFragmentSource::Image(ipc_sender.map(|renderer| Arc::new(Mutex::new(renderer))))
             }
         };
 
         CanvasFragmentInfo {
             source: source,
             dom_width: Au::from_px(data.width as i32),
             dom_height: Au::from_px(data.height as i32),
+            canvas_id: data.canvas_id,
         }
     }
 }
 
 #[derive(Clone)]
 pub struct SvgFragmentInfo {
     pub dom_width: Au,
     pub dom_height: Au,
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -25,17 +25,17 @@
 //!    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::canvas::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
+use canvas_traits::canvas::{CanvasGradientStop, CanvasId, 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 canvas_traits::webgl::{WebGLSLVersion, WebGLVersion};
 use cssparser::RGBA;
 use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
 use dom::abstractworker::SharedRt;
@@ -418,16 +418,17 @@ unsafe_no_jsmanaged_fields!(WebGLTexture
 unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
 unsafe_no_jsmanaged_fields!(WebGLVersion);
 unsafe_no_jsmanaged_fields!(WebGLSLVersion);
 unsafe_no_jsmanaged_fields!(MediaList);
 unsafe_no_jsmanaged_fields!(WebVRGamepadHand);
 unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
 unsafe_no_jsmanaged_fields!(InteractiveMetrics);
 unsafe_no_jsmanaged_fields!(InteractiveWindow);
+unsafe_no_jsmanaged_fields!(CanvasId);
 
 unsafe impl<'a> JSTraceable for &'a str {
     #[inline]
     unsafe fn trace(&self, _: *mut JSTracer) {
         // Do nothing
     }
 }
 
--- a/servo/components/script/dom/canvasrenderingcontext2d.rs
+++ b/servo/components/script/dom/canvasrenderingcontext2d.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::canvas::{Canvas2dMsg, CanvasMsg};
+use canvas_traits::canvas::{Canvas2dMsg, CanvasMsg, CanvasId};
 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::CanvasRenderingContext2DBinding;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
@@ -73,16 +73,17 @@ pub struct CanvasRenderingContext2D {
     /// Any missing image URLs.
     missing_image_urls: DomRefCell<Vec<ServoUrl>>,
     /// The base URL for resolving CSS image URL values.
     /// Needed because of https://github.com/servo/servo/issues/17625
     base_url: ServoUrl,
     state: DomRefCell<CanvasContextState>,
     saved_states: DomRefCell<Vec<CanvasContextState>>,
     origin_clean: Cell<bool>,
+    canvas_id: CanvasId,
 }
 
 #[must_root]
 #[derive(Clone, JSTraceable, MallocSizeOf)]
 struct CanvasContextState {
     global_alpha: f64,
     global_composition: CompositionOrBlending,
     image_smoothing_enabled: bool,
@@ -128,28 +129,29 @@ impl CanvasRenderingContext2D {
                          base_url: ServoUrl,
                          size: Size2D<i32>)
                          -> CanvasRenderingContext2D {
         debug!("Creating new canvas rendering context.");
         let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
         let script_to_constellation_chan = global.script_to_constellation_chan();
         debug!("Asking constellation to create new canvas thread.");
         script_to_constellation_chan.send(ScriptMsg::CreateCanvasPaintThread(size, sender)).unwrap();
-        let ipc_renderer = receiver.recv().unwrap();
+        let (ipc_renderer, canvas_id) = receiver.recv().unwrap();
         debug!("Done.");
         CanvasRenderingContext2D {
             reflector_: Reflector::new(),
             ipc_renderer: ipc_renderer,
             canvas: canvas.map(Dom::from_ref),
             image_cache: image_cache,
             missing_image_urls: DomRefCell::new(Vec::new()),
             base_url: base_url,
             state: DomRefCell::new(CanvasContextState::new()),
             saved_states: DomRefCell::new(Vec::new()),
             origin_clean: Cell::new(true),
+            canvas_id: canvas_id,
         }
     }
 
     pub fn new(global: &GlobalScope,
                canvas: &HTMLCanvasElement,
                size: Size2D<i32>)
                -> DomRoot<CanvasRenderingContext2D> {
         let window = window_from_node(canvas);
@@ -160,36 +162,34 @@ impl CanvasRenderingContext2D {
         ));
         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::Recreate(size))
+            .send(CanvasMsg::Recreate(size, self.get_canvas_id()))
             .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();
     }
 
     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
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetTransform(self.state.borrow().transform)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetTransform(self.state.borrow().transform))
     }
 
     // It is used by DrawImage to calculate the size of the source and destination rectangles based
     // on the drawImage call arguments
     // source rectangle = area of the original image to be copied
     // destination rectangle = area of the destination canvas where the source image is going to be drawn
     fn adjust_source_dest_rects(&self,
                                 image_size: Size2D<f64>,
@@ -358,33 +358,34 @@ impl CanvasRenderingContext2D {
 
         if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
             return Ok(());
         }
 
         let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
 
         if self.canvas.as_ref().map_or(false, |c| &**c == canvas) {
-            let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageSelf(
+            self.send_canvas_2d_msg(Canvas2dMsg::DrawImageSelf(
                 image_size, dest_rect, source_rect, smoothing_enabled));
-            self.ipc_renderer.send(msg).unwrap();
         } else {
             let context = match canvas.get_or_init_2d_context() {
                 Some(context) => context,
                 None => return Err(Error::InvalidState),
             };
 
             let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
             let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageInOther(
                 self.ipc_renderer.clone(),
+                self.get_canvas_id(),
                 image_size,
                 dest_rect,
                 source_rect,
                 smoothing_enabled,
-                sender));
+                sender),
+                context.get_canvas_id());
 
             let renderer = context.get_ipc_renderer();
             renderer.send(msg).unwrap();
             receiver.recv().unwrap();
         };
 
         self.mark_as_dirty();
         Ok(())
@@ -448,23 +449,23 @@ impl CanvasRenderingContext2D {
                                                                      dw,
                                                                      dh);
 
         if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
             return Ok(());
         }
 
         let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::DrawImage(
+        self.send_canvas_2d_msg(Canvas2dMsg::DrawImage(
             image_data.into(),
             image_size,
             dest_rect,
             source_rect,
             smoothing_enabled,
-        ))).unwrap();
+        ));
         self.mark_as_dirty();
         Ok(())
     }
 
     fn fetch_image_data(&self, url: ServoUrl) -> Option<(Vec<u8>, Size2D<i32>)> {
         let img = match self.request_image_from_cache(url) {
             ImageResponse::Loaded(img, _) => img,
             ImageResponse::PlaceholderLoaded(_, _) |
@@ -552,39 +553,54 @@ impl CanvasRenderingContext2D {
                 },
                 _ => Err(())
             }
         } else {
             Err(())
         }
     }
 
+    pub fn get_canvas_id(&self) -> CanvasId {
+        self.canvas_id.clone()
+    }
+
+    pub fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
+        self.ipc_renderer.send(CanvasMsg::Canvas2d(msg, self.get_canvas_id())).unwrap()
+    }
+
     pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
         self.ipc_renderer.clone()
     }
 
     pub fn origin_is_clean(&self) -> bool {
         self.origin_clean.get()
     }
 
     fn set_origin_unclean(&self) {
         self.origin_clean.set(false)
     }
 }
 
 pub trait LayoutCanvasRenderingContext2DHelpers {
     #[allow(unsafe_code)]
     unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg>;
+    #[allow(unsafe_code)]
+    unsafe fn get_canvas_id(&self) -> CanvasId;
 }
 
 impl LayoutCanvasRenderingContext2DHelpers for LayoutDom<CanvasRenderingContext2D> {
     #[allow(unsafe_code)]
     unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
         (*self.unsafe_get()).ipc_renderer.clone()
     }
+
+    #[allow(unsafe_code)]
+    unsafe fn get_canvas_id(&self) -> CanvasId {
+        (*self.unsafe_get()).canvas_id.clone()
+    }
 }
 
 // We add a guard to each of methods by the spec:
 // http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/
 //
 // > Except where otherwise specified, for the 2D context interface,
 // > any method call with a numeric argument whose value is infinite or a NaN value must be ignored.
 //
@@ -597,26 +613,26 @@ impl CanvasRenderingContext2DMethods for
         // This method is not called from a paint worklet rendering context,
         // so it's OK to panic if self.canvas is None.
         DomRoot::from_ref(self.canvas.as_ref().expect("No canvas."))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-save
     fn Save(&self) {
         self.saved_states.borrow_mut().push(self.state.borrow().clone());
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SaveContext)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::SaveContext);
     }
 
     #[allow(unrooted_must_root)]
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore
     fn Restore(&self) {
         let mut saved_states = self.saved_states.borrow_mut();
         if let Some(state) = saved_states.pop() {
             self.state.borrow_mut().clone_from(&state);
-            self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::RestoreContext)).unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::RestoreContext);
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale
     fn Scale(&self, x: f64, y: f64) {
         if !(x.is_finite() && y.is_finite()) {
             return;
         }
@@ -691,114 +707,104 @@ impl CanvasRenderingContext2DMethods for
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha
     fn SetGlobalAlpha(&self, alpha: f64) {
         if !alpha.is_finite() || alpha > 1.0 || alpha < 0.0 {
             return;
         }
 
         self.state.borrow_mut().global_alpha = alpha;
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetGlobalAlpha(alpha as f32)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalAlpha(alpha as f32))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation
     fn GlobalCompositeOperation(&self) -> DOMString {
         let state = self.state.borrow();
         match state.global_composition {
             CompositionOrBlending::Composition(op) => DOMString::from(op.to_str()),
             CompositionOrBlending::Blending(op) => DOMString::from(op.to_str()),
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation
     fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
         if let Ok(op) = CompositionOrBlending::from_str(&op_str) {
             self.state.borrow_mut().global_composition = op;
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetGlobalComposition(op)))
-                .unwrap()
+            self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalComposition(op))
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect
     fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
         if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
-            self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::FillRect(rect))).unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::FillRect(rect));
             self.mark_as_dirty();
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect
     fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
         if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::ClearRect(rect)))
-                .unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::ClearRect(rect));
             self.mark_as_dirty();
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect
     fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
         if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::StrokeRect(rect)))
-                .unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::StrokeRect(rect));
             self.mark_as_dirty();
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath
     fn BeginPath(&self) {
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::BeginPath)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::BeginPath);
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath
     fn ClosePath(&self) {
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::ClosePath)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::ClosePath);
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill
     fn Fill(&self, _: CanvasFillRule) {
         // TODO: Process fill rule
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Fill)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Fill);
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke
     fn Stroke(&self) {
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Stroke)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Stroke);
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip
     fn Clip(&self, _: CanvasFillRule) {
         // TODO: Process fill rule
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Clip)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Clip);
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath
     fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
         let fill_rule = match fill_rule {
             CanvasFillRule::Nonzero => FillRule::Nonzero,
             CanvasFillRule::Evenodd => FillRule::Evenodd,
         };
         let (sender, receiver) = ipc::channel::<bool>(self.global().time_profiler_chan().clone()).unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender)))
-            .unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender));
         receiver.recv().unwrap()
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext
     fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
         let parsed_text: String = text.into();
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::FillText(parsed_text, x, y, max_width))).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width));
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
     fn DrawImage(&self,
                  image: CanvasImageSource,
                  dx: f64,
                  dy: f64)
@@ -853,122 +859,109 @@ impl CanvasRenderingContext2DMethods for
                         Some(dh))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto
     fn MoveTo(&self, x: f64, y: f64) {
         if !(x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto
     fn LineTo(&self, x: f64, y: f64) {
         if !(x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect
     fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
         if [x, y, width, height].iter().all(|val| val.is_finite()) {
             let rect = Rect::new(Point2D::new(x as f32, y as f32),
                                  Size2D::new(width as f32, height as f32));
-            let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Rect(rect));
-            self.ipc_renderer.send(msg).unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::Rect(rect));
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto
     fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
         if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::QuadraticCurveTo(Point2D::new(cpx as f32,
-                                                                                 cpy as f32),
-                                                                    Point2D::new(x as f32,
-                                                                                 y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::QuadraticCurveTo(Point2D::new(cpx as f32,
+                                                                           cpy as f32),
+                                                              Point2D::new(x as f32,
+                                                                           y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto
     fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
         if !(cp1x.is_finite() && cp1y.is_finite() && cp2x.is_finite() && cp2y.is_finite() &&
              x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::BezierCurveTo(Point2D::new(cp1x as f32,
-                                                                              cp1y as f32),
-                                                                 Point2D::new(cp2x as f32,
-                                                                              cp2y as f32),
-                                                                 Point2D::new(x as f32, y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::BezierCurveTo(Point2D::new(cp1x as f32,
+                                                                        cp1y as f32),
+                                                           Point2D::new(cp2x as f32,
+                                                                        cp2y as f32),
+                                                           Point2D::new(x as f32, y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc
     fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
         if !([x, y, r, start, end].iter().all(|x| x.is_finite())) {
             return Ok(());
         }
 
         if r < 0.0 {
             return Err(Error::IndexSize);
         }
 
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Arc(Point2D::new(x as f32, y as f32),
-                                                       r as f32,
-                                                       start as f32,
-                                                       end as f32,
-                                                       ccw));
-
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Arc(Point2D::new(x as f32, y as f32),
+                                                 r as f32,
+                                                 start as f32,
+                                                 end as f32,
+                                                 ccw));
         Ok(())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto
     fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
         if !([cp1x, cp1y, cp2x, cp2y, r].iter().all(|x| x.is_finite())) {
             return Ok(());
         }
         if r < 0.0 {
             return Err(Error::IndexSize);
         }
 
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::ArcTo(Point2D::new(cp1x as f32, cp1y as f32),
-                                                         Point2D::new(cp2x as f32, cp2y as f32),
-                                                         r as f32));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::ArcTo(Point2D::new(cp1x as f32, cp1y as f32),
+                                                   Point2D::new(cp2x as f32, cp2y as f32),
+                                                   r as f32));
         Ok(())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse
     fn Ellipse(&self, x: f64, y: f64, rx: f64, ry: f64, rotation: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
         if !([x, y, rx, ry, rotation, start, end].iter().all(|x| x.is_finite())) {
             return Ok(());
         }
         if rx < 0.0 || ry < 0.0 {
             return Err(Error::IndexSize);
         }
 
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Ellipse(Point2D::new(x as f32, y as f32),
-                                                       rx as f32,
-                                                       ry as f32,
-                                                       rotation as f32,
-                                                       start as f32,
-                                                       end as f32,
-                                                       ccw));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Ellipse(Point2D::new(x as f32, y as f32),
+                                                     rx as f32,
+                                                     ry as f32,
+                                                     rotation as f32,
+                                                     start as f32,
+                                                     end as f32,
+                                                     ccw));
         Ok(())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled
     fn ImageSmoothingEnabled(&self) -> bool {
         let state = self.state.borrow();
         state.image_smoothing_enabled
     }
@@ -996,35 +989,29 @@ impl CanvasRenderingContext2DMethods for
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
     fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
         match value {
             StringOrCanvasGradientOrCanvasPattern::String(string) => {
                 if let Ok(rgba) = self.parse_color(&string) {
                     self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
-                    self.ipc_renderer
-                        .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle(
-                                    FillOrStrokeStyle::Color(rgba))))
-                        .unwrap();
+                    self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(
+                        FillOrStrokeStyle::Color(rgba)));
                 }
             },
             StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => {
                 self.state.borrow_mut().stroke_style =
                     CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
             },
             StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => {
                 self.state.borrow_mut().stroke_style =
                     CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetStrokeStyle(pattern.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(pattern.to_fill_or_stroke_style()));
                 if !pattern.origin_is_clean() {
                     self.set_origin_unclean();
                 }
             }
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
@@ -1045,35 +1032,29 @@ impl CanvasRenderingContext2DMethods for
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
     fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
         match value {
             StringOrCanvasGradientOrCanvasPattern::String(string) => {
                 if let Ok(rgba) = self.parse_color(&string) {
                     self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba);
-                    self.ipc_renderer
-                        .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetFillStyle(
-                                    FillOrStrokeStyle::Color(rgba))))
-                        .unwrap()
+                    self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(
+                        FillOrStrokeStyle::Color(rgba)))
                 }
             }
             StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => {
                 self.state.borrow_mut().fill_style =
                     CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetFillStyle(gradient.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(gradient.to_fill_or_stroke_style()));
             }
             StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => {
                 self.state.borrow_mut().fill_style =
                     CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetFillStyle(pattern.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(pattern.to_fill_or_stroke_style()));
                 if !pattern.origin_is_clean() {
                     self.set_origin_unclean();
                 }
             }
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata
@@ -1127,30 +1108,28 @@ impl CanvasRenderingContext2DMethods for
         let sh = cmp::max(1, sh.to_u32().unwrap());
         let sw = cmp::max(1, sw.to_u32().unwrap());
 
         let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
         let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
                                   Size2D::new(sw as i32, sh as i32));
         let canvas_size = self.canvas.as_ref().map(|c| c.get_size()).unwrap_or(Size2D::zero());
         let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender)))
-            .unwrap();
-        let mut data = Vec::from(receiver.recv().unwrap());
+        self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender));
+        let mut data = receiver.recv().unwrap();
 
         // Un-premultiply alpha
         for chunk in data.chunks_mut(4) {
             let alpha = chunk[3] as usize;
             chunk[0] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[0] as usize];
             chunk[1] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[1] as usize];
             chunk[2] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[2] as usize];
         }
 
-        ImageData::new(&self.global(), sw, sh, Some(data))
+        ImageData::new(&self.global(), sw, sh, Some(data.to_vec()))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata
     fn PutImageData(&self, imagedata: &ImageData, dx: Finite<f64>, dy: Finite<f64>) {
         self.PutImageData_(imagedata,
                            dx,
                            dy,
                            Finite::wrap(0f64),
@@ -1169,23 +1148,20 @@ impl CanvasRenderingContext2DMethods for
                      dirty_width: Finite<f64>,
                      dirty_height: Finite<f64>) {
         let data = imagedata.get_data_array();
         let offset = Vector2D::new(*dx, *dy);
         let image_data_size = Size2D::new(imagedata.Width() as f64, imagedata.Height() as f64);
 
         let dirty_rect = Rect::new(Point2D::new(*dirty_x, *dirty_y),
                                    Size2D::new(*dirty_width, *dirty_height));
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::PutImageData(
-            data.into(),
-            offset,
-            image_data_size,
-            dirty_rect,
-        ));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(data.into(),
+                                                                offset,
+                                                                image_data_size,
+                                                                dirty_rect));
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient
     fn CreateLinearGradient(&self,
                             x0: Finite<f64>,
                             y0: Finite<f64>,
                             x1: Finite<f64>,
@@ -1277,19 +1253,17 @@ impl CanvasRenderingContext2DMethods for
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth
     fn SetLineWidth(&self, width: f64) {
         if !width.is_finite() || width <= 0.0 {
             return;
         }
 
         self.state.borrow_mut().line_width = width;
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineWidth(width as f32)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetLineWidth(width as f32))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap
     fn LineCap(&self) -> CanvasLineCap {
         match self.state.borrow().line_cap {
             LineCapStyle::Butt => CanvasLineCap::Butt,
             LineCapStyle::Round => CanvasLineCap::Round,
             LineCapStyle::Square => CanvasLineCap::Square,
@@ -1299,17 +1273,17 @@ impl CanvasRenderingContext2DMethods for
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap
     fn SetLineCap(&self, cap: CanvasLineCap) {
         let line_cap = match cap {
             CanvasLineCap::Butt => LineCapStyle::Butt,
             CanvasLineCap::Round => LineCapStyle::Round,
             CanvasLineCap::Square => LineCapStyle::Square,
         };
         self.state.borrow_mut().line_cap = line_cap;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineCap(line_cap))).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::SetLineCap(line_cap));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin
     fn LineJoin(&self) -> CanvasLineJoin {
         match self.state.borrow().line_join {
             LineJoinStyle::Round => CanvasLineJoin::Round,
             LineJoinStyle::Bevel => CanvasLineJoin::Bevel,
             LineJoinStyle::Miter => CanvasLineJoin::Miter,
@@ -1319,100 +1293,96 @@ impl CanvasRenderingContext2DMethods for
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin
     fn SetLineJoin(&self, join: CanvasLineJoin) {
         let line_join = match join {
             CanvasLineJoin::Round => LineJoinStyle::Round,
             CanvasLineJoin::Bevel => LineJoinStyle::Bevel,
             CanvasLineJoin::Miter => LineJoinStyle::Miter,
         };
         self.state.borrow_mut().line_join = line_join;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineJoin(line_join))).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::SetLineJoin(line_join));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit
     fn MiterLimit(&self) -> f64 {
         let state = self.state.borrow();
         state.miter_limit
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit
     fn SetMiterLimit(&self, limit: f64) {
         if !limit.is_finite() || limit <= 0.0 {
             return;
         }
 
         self.state.borrow_mut().miter_limit = limit;
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetMiterLimit(limit as f32)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetMiterLimit(limit as f32))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
     fn ShadowOffsetX(&self) -> f64 {
         self.state.borrow().shadow_offset_x
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
     fn SetShadowOffsetX(&self, value: f64) {
         if !value.is_finite() || value == self.state.borrow().shadow_offset_x {
             return;
         }
         self.state.borrow_mut().shadow_offset_x = value;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowOffsetX(value))).unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetX(value))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety
     fn ShadowOffsetY(&self) -> f64 {
         self.state.borrow().shadow_offset_y
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety
     fn SetShadowOffsetY(&self, value: f64) {
         if !value.is_finite() || value == self.state.borrow().shadow_offset_y {
             return;
         }
         self.state.borrow_mut().shadow_offset_y = value;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowOffsetY(value))).unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetY(value))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur
     fn ShadowBlur(&self) -> f64 {
         self.state.borrow().shadow_blur
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur
     fn SetShadowBlur(&self, value: f64) {
         if !value.is_finite() || value < 0f64 || value == self.state.borrow().shadow_blur {
             return;
         }
         self.state.borrow_mut().shadow_blur = value;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowBlur(value))).unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetShadowBlur(value))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
     fn ShadowColor(&self) -> DOMString {
         let mut result = String::new();
         serialize(&self.state.borrow().shadow_color, &mut result).unwrap();
         DOMString::from(result)
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
     fn SetShadowColor(&self, value: DOMString) {
         if let Ok(color) = parse_color(&value) {
             self.state.borrow_mut().shadow_color = color;
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowColor(color)))
-                .unwrap()
+            self.send_canvas_2d_msg(Canvas2dMsg::SetShadowColor(color))
         }
     }
 }
 
 impl Drop for CanvasRenderingContext2D {
     fn drop(&mut self) {
-        if let Err(err) = self.ipc_renderer.send(CanvasMsg::Close) {
+        if let Err(err) = self.ipc_renderer.send(CanvasMsg::Close(self.get_canvas_id())) {
             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::canvas::{CanvasMsg, FromScriptMsg};
+use canvas_traits::canvas::{CanvasMsg, CanvasId, FromScriptMsg};
 use canvas_traits::webgl::WebGLVersion;
 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, RenderingContext};
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
 use dom::bindings::conversions::ConversionResult;
@@ -100,16 +100,17 @@ impl HTMLCanvasElement {
         }
     }
 }
 
 pub trait LayoutHTMLCanvasElementHelpers {
     fn data(&self) -> HTMLCanvasData;
     fn get_width(&self) -> LengthOrPercentageOrAuto;
     fn get_height(&self) -> LengthOrPercentageOrAuto;
+    fn get_canvas_id_for_layout(&self) -> CanvasId;
 }
 
 impl LayoutHTMLCanvasElementHelpers for LayoutDom<HTMLCanvasElement> {
     #[allow(unsafe_code)]
     fn data(&self) -> HTMLCanvasData {
         unsafe {
             let canvas = &*self.unsafe_get();
             let source = match canvas.context.borrow_for_layout().as_ref() {
@@ -128,16 +129,17 @@ impl LayoutHTMLCanvasElementHelpers for 
             };
 
             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 {
                 source: source,
                 width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
                 height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
+                canvas_id: self.get_canvas_id_for_layout(),
             }
         }
     }
 
     #[allow(unsafe_code)]
     fn get_width(&self) -> LengthOrPercentageOrAuto {
         unsafe {
             (&*self.upcast::<Element>().unsafe_get())
@@ -151,16 +153,28 @@ impl LayoutHTMLCanvasElementHelpers for 
     fn get_height(&self) -> LengthOrPercentageOrAuto {
         unsafe {
             (&*self.upcast::<Element>().unsafe_get())
                 .get_attr_for_layout(&ns!(), &local_name!("height"))
                 .map(AttrValue::as_uint_px_dimension)
                 .unwrap_or(LengthOrPercentageOrAuto::Auto)
         }
     }
+
+    #[allow(unsafe_code)]
+    fn get_canvas_id_for_layout(&self) -> CanvasId {
+        unsafe {
+            let canvas = &*self.unsafe_get();
+            if let &Some(CanvasContext::Context2d(ref context)) = canvas.context.borrow_for_layout() {
+                context.to_layout().get_canvas_id()
+            } else {
+                CanvasId(0)
+            }
+        }
+    }
 }
 
 
 impl HTMLCanvasElement {
     pub fn get_or_init_2d_context(&self) -> Option<DomRoot<CanvasRenderingContext2D>> {
         if self.context.borrow().is_none() {
             let window = window_from_node(self);
             let size = self.get_size();
@@ -256,17 +270,17 @@ impl HTMLCanvasElement {
 
         if size.width == 0 || size.height == 0 {
             return None
         }
 
         let data = match self.context.borrow().as_ref() {
             Some(&CanvasContext::Context2d(ref context)) => {
                 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
-                let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender));
+                let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender), context.get_canvas_id());
                 context.get_ipc_renderer().send(msg).unwrap();
 
                 receiver.recv().unwrap()?.into()
             },
             Some(&CanvasContext::WebGL(_)) => {
                 // TODO: add a method in WebGLRenderingContext to get the pixels.
                 return None;
             },
--- a/servo/components/script/dom/paintrenderingcontext2d.rs
+++ b/servo/components/script/dom/paintrenderingcontext2d.rs
@@ -54,17 +54,17 @@ impl PaintRenderingContext2D {
 
     pub fn new(global: &PaintWorkletGlobalScope) -> DomRoot<PaintRenderingContext2D> {
         reflect_dom_object(Box::new(PaintRenderingContext2D::new_inherited(global)),
                            global,
                            PaintRenderingContext2DBinding::Wrap)
     }
 
     pub fn send_data(&self, sender: IpcSender<CanvasImageData>) {
-        let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender));
+        let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender), self.context.get_canvas_id());
         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,
--- a/servo/components/script_layout_interface/lib.rs
+++ b/servo/components/script_layout_interface/lib.rs
@@ -36,17 +36,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::canvas::CanvasMsg;
+use canvas_traits::canvas::{CanvasMsg, CanvasId};
 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::ptr::NonNull;
 use std::sync::atomic::AtomicIsize;
 use style::data::ElementData;
@@ -128,16 +128,17 @@ pub enum HTMLCanvasDataSource {
     WebGL(webrender_api::ImageKey),
     Image(Option<IpcSender<CanvasMsg>>)
 }
 
 pub struct HTMLCanvasData {
     pub source: HTMLCanvasDataSource,
     pub width: u32,
     pub height: u32,
+    pub canvas_id: CanvasId,
 }
 
 pub struct SVGSVGData {
     pub width: u32,
     pub height: u32,
 }
 
 /// The address of a node known to be valid. These are sent from script to layout.
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -6,17 +6,17 @@ use AnimationState;
 use CompositorEvent;
 use DocumentState;
 use IFrameLoadInfo;
 use IFrameLoadInfoWithData;
 use LayoutControlMsg;
 use LoadData;
 use WorkerGlobalScopeInit;
 use WorkerScriptLoadOrigin;
-use canvas_traits::canvas::CanvasMsg;
+use canvas_traits::canvas::{CanvasMsg, CanvasId};
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::{Size2D, TypedSize2D};
 use gfx_traits::Epoch;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use msg::constellation_msg::{BrowsingContextId, PipelineId, TraversalDirection};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use net_traits::CoreResourceMsg;
 use net_traits::request::RequestInit;
@@ -74,17 +74,17 @@ pub enum ScriptMsg {
     InitiateNavigateRequest(RequestInit, /* cancellation_chan */ IpcReceiver<()>),
     /// 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>>),
+    CreateCanvasPaintThread(Size2D<i32>, IpcSender<(IpcSender<CanvasMsg>, CanvasId)>),
     /// 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/tests/html/test_canvas_drawimage_canvas.html
+++ b/servo/tests/html/test_canvas_drawimage_canvas.html
@@ -1,9 +1,9 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <html>
 <head>
   <meta charset="utf-8">
 </head>
 <style type=''>
 html {
   font-family: helvetica, sans-serif;
 }
@@ -125,17 +125,17 @@ imageSource = {
   height: 50
 };
 
 destCanvas = {
   width: 100,
   height: 50
 };
 
-drawImage(0, 0);
+    drawImage(0, 0);
 
 function renderExample(title) {
   var container = document.createElement('div');
   container.id = 'example' + examplesNum++;
   container.setAttribute('class', 'example');
 
   var h2 = document.createElement('h2');
   h2.textContent = title;
new file mode 100644
--- /dev/null
+++ b/servo/tests/html/test_canvas_drawimage_canvas_timed.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<style type=''>
+html {
+  font-family: helvetica, sans-serif;
+}
+
+canvas {
+  padding: 15px;
+  margin: 10px;
+}
+
+.example {
+  display: inline-block;
+  width: 280px;
+  margin-top: 50px;
+  margin-right: 50px;
+}
+
+.title {
+  text-align: center;
+}
+
+.description {
+  width: 100%;
+}
+
+.description div {
+  display: inline-block;
+  text-align: center;
+  font-size: 13px;
+}
+
+.description .source {
+  width: 100px;
+}
+
+.description .target {
+  width: 100px;
+}
+
+.example-title {
+  text-align: center;
+  font-size: 16px;
+}
+
+.error {
+  color: red;
+}
+
+</style>
+<body>
+    <h1 class="title">Timed DrawImage canvas to canvas</h1>
+    <h4 class="title", id="Test Result">This text is where the timing result will go...</h4>
+</body>
+
+<script type="text/javascript">
+
+var smoothingEnabled = false;
+var examplesNum = 0;
+
+var imageSource = {
+  width: 50,
+  height: 50
+};
+
+var destCanvas = {
+  width: 100,
+  height: 100
+};
+
+    var t0 = performance.now();     //save starting time
+    drawImage(25, 25);            // The source canvas will copied to the 25,25 position of the destination canvas
+    var t1 = performance.now();     //save ending time
+    document.getElementById('Test Result').innerHTML = "DrawImage took " + (t1 - t0) + " milliseconds."; //print to browser
+    console.log("DrawImage took " + (t1 - t0) + " milliseconds.");  //print to console
+
+function renderExample(title) {
+  var container = document.createElement('div');
+  container.id = 'example' + examplesNum++;
+  container.setAttribute('class', 'example');
+
+  var h2 = document.createElement('h2');
+  h2.textContent = title;
+  h2.setAttribute('class', 'example-title');
+  container.appendChild(h2);
+
+  var div1 = document.createElement('div');
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+  div1.appendChild(canvas1);
+  div1.appendChild(canvas2);
+  container.appendChild(div1);
+
+  var div2 = document.createElement('div');
+  div2.setAttribute('class', 'description');
+  var source = document.createElement('div');
+  source.textContent = ' Source (' + imageSource.width + ',' + imageSource.height + ')';
+  source.setAttribute('class', 'source');
+
+  var arrow = document.createElement('div');
+  arrow.textContent = ' -> ';
+  arrow.setAttribute('class', 'arrow');
+
+  var target = document.createElement('div');
+  target.textContent = 'Target (' + destCanvas.width + ',' + destCanvas.height + ')';
+  target.setAttribute('class', 'target');
+
+  div2.appendChild(source);
+  div2.appendChild(arrow);
+  div2.appendChild(target);
+  container.appendChild(div2);
+
+  return container;
+}
+
+function drawImage() {
+  var args = Array.prototype.slice.call(arguments);
+
+  var div = renderExample('drawImage(' + args.toString() + ')');
+  var canvasEls = div.querySelectorAll('canvas');
+  var canvas1 = canvasEls[0];
+  var canvas2 = canvasEls[1];
+
+  canvas1.width = imageSource.width;
+  canvas1.height = imageSource.height;
+
+  var ctx1 = canvas1.getContext('2d');
+  ctx1.fillStyle = "#00FFFF";
+  ctx1.fillRect(0, 0, imageSource.width, imageSource.height);
+
+  ctx1.fillStyle = "#000000";
+  ctx1.fillRect(5,5,40,40);
+  ctx1.clearRect(10,10,30,30);
+  ctx1.strokeRect(15,15,20,20);
+
+  canvas2.width = destCanvas.width;
+  canvas2.height = destCanvas.height;
+
+  var ctx2 = canvas2.getContext('2d');
+  ctx2.fillStyle = "#FF0000";
+  ctx2.fillRect(0, 0,  destCanvas.width, destCanvas.height);
+  ctx2.imageSmoothingEnabled = smoothingEnabled;
+
+  args.unshift(canvas1);
+  try {
+    ctx2.drawImage.apply(ctx2, args);
+  }
+  catch(err) {
+    var title = div.querySelector('.example-title');
+    var error = document.createElement('h2');
+    error.setAttribute('class', 'example-title error');
+    div.insertBefore(error, title);
+    error.textContent += "Call Failed: " + err.message;
+  }
+
+  document.body.appendChild(div);
+};
+
+</script>
+</body>
+</html>