First-pass for adding HTML5's Canvas.

This is not the final version yet (among other issues, the location is up for debate) but I would like to give people a chance to comment on it at this stage.

Review at http://gwt-code-reviews.appspot.com/1082801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9304 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/canvas/Canvas.gwt.xml b/user/src/com/google/gwt/canvas/Canvas.gwt.xml
new file mode 100644
index 0000000..3b1da45
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/Canvas.gwt.xml
@@ -0,0 +1,20 @@
+<!--
+  Copyright 2010 Google Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  use this file except in compliance with the License. You may obtain a copy of
+  the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  License for the specific language governing permissions and limitations under
+  the License.
+-->
+<module>
+  <inherits name="com.google.gwt.user.User"/>
+  <inherits name="com.google.gwt.canvas.dom.DOM"/>
+  <source path="client"/>
+</module>
diff --git a/user/src/com/google/gwt/canvas/client/Canvas.java b/user/src/com/google/gwt/canvas/client/Canvas.java
new file mode 100644
index 0000000..22903f0
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/client/Canvas.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.client;
+
+import com.google.gwt.canvas.dom.client.Context;
+import com.google.gwt.canvas.dom.client.Context2d;
+import com.google.gwt.dom.client.CanvasElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.IsSupported;
+import com.google.gwt.user.client.ui.FocusWidget;
+
+/**
+ * A widget representing a &lt;canvas&gt; element.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ * 
+ * This widget may not be supported on all browsers, see {@link IsSupported}
+ */
+public class Canvas extends FocusWidget implements IsSupported {
+  /**
+   * Runtime check for whether the canvas element is supported in this browser.
+   * See {@link IsSupported}
+   * 
+   * @return whether the canvas element is supported
+   */
+  public static final native boolean isSupported() /*-{
+    return !!$doc.createElement('canvas').getContext;
+  }-*/;
+
+  /**
+   * Creates a Canvas.
+   */
+  public Canvas() {
+    setElement(Document.get().createCanvasElement());
+  }
+
+  /**
+   * Returns the attached Canvas Element.
+   * 
+   * @return the Canvas Element
+   */
+  public CanvasElement getCanvasElement() {
+    return this.getElement().cast();
+  }
+
+  /**
+   * Gets the rendering context that may be used to draw on this canvas.
+   * 
+   * @return the canvas rendering context
+   */
+  public Context getContext(String contextId) {
+    return getCanvasElement().getContext(contextId);
+  }
+
+  /**
+   * Returns a 2D rendering context.
+   * 
+   * This is a convenience method, @see {@link #getContext(String)}
+   * 
+   * @return a 2D canvas rendering context
+   */
+  public Context2d getContext2d() {
+    return getCanvasElement().getContext2d();
+  }
+
+  /**
+   * Gets the height of the internal canvas coordinate space.
+   * 
+   * @return the height, in pixels
+   */
+  public int getCoordinateSpaceHeight() {
+    return getCanvasElement().getHeight();
+  }
+
+  /**
+   * Gets the width of the internal canvas coordinate space.
+   * 
+   * @return the width, in pixels
+   */
+  public int getCoordinateSpaceWidth() {
+    return getCanvasElement().getWidth();
+  }
+
+  /**
+   * Sets the height of the internal canvas coordinate space.
+   * 
+   * @param height the height, in pixels
+   */
+  public void setCoordinateSpaceHeight(int height) {
+    getCanvasElement().setHeight(height);
+  }
+
+  /**
+   * Sets the width of the internal canvas coordinate space.
+   * 
+   * @param width the width, in pixels
+   */
+  public void setCoordinateSpaceWidth(int width) {
+    getCanvasElement().setWidth(width);
+  }
+
+  /**
+   * Returns a data URL for the current content of the canvas element.
+   * 
+   * @return a data URL for the current content of this element.
+   */
+  public String toDataUrl() {
+    return getCanvasElement().toDataUrl();
+  }
+
+  /**
+   * Returns a data URL for the current content of the canvas element, with a specified type.
+   * 
+   * @param type the type of the data url, e.g., image/jpeg or image/png.
+   * @return a data URL for the current content of this element with the specified type.
+   */
+  public String toDataUrl(String type) {
+    return getCanvasElement().toDataUrl(type);
+  }
+}
diff --git a/user/src/com/google/gwt/canvas/dom/DOM.gwt.xml b/user/src/com/google/gwt/canvas/dom/DOM.gwt.xml
new file mode 100644
index 0000000..eee5d89
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/DOM.gwt.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 2010 Google Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  use this file except in compliance with the License. You may obtain a copy of
+  the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  License for the specific language governing permissions and limitations under
+  the License.
+-->
+<module>
+  <source path="client"/>
+  <inherits name="com.google.gwt.dom.DOM"/>
+</module>
diff --git a/user/src/com/google/gwt/canvas/dom/client/CanvasGradient.java b/user/src/com/google/gwt/canvas/dom/client/CanvasGradient.java
new file mode 100644
index 0000000..c8d2341
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CanvasGradient.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * Gradient object used with {@link Context2d}.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ * 
+ * This class serves as a reference to a gradient created using {@link Context2d#createPattern}
+ * 
+ * @see <a href="http://www.w3.org/TR/2dcontext/#canvasgradient">CanvasGradient</a>
+ */
+public class CanvasGradient extends FillStrokeStyle {
+
+  protected CanvasGradient() {
+  }
+
+  /**
+   * Adds a new color stop to the gradient.
+   * 
+   * @param offset value between 0 and 1 for where the color stop is located.
+   * @param color color at the stop.
+   */
+  public final native void addColorStop(float offset, String color) /*-{
+    this.addColorStop(offset, color);
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/CanvasPattern.java b/user/src/com/google/gwt/canvas/dom/client/CanvasPattern.java
new file mode 100644
index 0000000..c0e3f17
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CanvasPattern.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * Pattern object used with {@link Context2d}.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ * 
+ * Note that this class has no methods. It simply serves as a reference to a
+ * pattern created using {@link Context2d#createPattern}.
+ * 
+ * @see <a href="http://www.w3.org/TR/2dcontext/#canvaspattern">CanvasPattern</a>
+ */
+public class CanvasPattern extends FillStrokeStyle {
+
+  protected CanvasPattern() {
+  }
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/CanvasPixelArray.java b/user/src/com/google/gwt/canvas/dom/client/CanvasPixelArray.java
new file mode 100644
index 0000000..d792752
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CanvasPixelArray.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Array-like object holding the actual image data for an ImageData object. For each pixel, 
+ * this object contains a red, green, blue and alpha value between 0 and 255 (in this order).
+ * Note that we use ints here to represent the data to avoid complexities stemming from
+ * bytes being signed in Java.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class CanvasPixelArray extends JavaScriptObject {
+
+  protected CanvasPixelArray() {
+  }
+
+  /**
+   * Returns the data value at index i.
+   */
+  public final native int get(int i) /*-{
+    return this[i] || 0;
+  }-*/;
+
+  /**
+   * Returns the length of the array.
+   */
+  public final native int getLength() /*-{
+    return this.length;
+  }-*/;
+
+  /**
+   * Sets the data value at position i to the given value.
+   * 
+   * Most browsers will clamp this value to the range 0...255 that is not enforced in this
+   * implementation.
+   * 
+   * @param i index to set.
+   * @param value value to set (use values from 0 to 255)
+   */
+  public final native void set(int i, int value) /*-{
+    // FF3.0 doesn't clamp the range. We don't manually clamp it to maximize performance.
+    this[i] = value;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/Context.java b/user/src/com/google/gwt/canvas/dom/client/Context.java
new file mode 100644
index 0000000..0259462
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/Context.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * Rendering Context interface used to draw on a {@link CanvasElement}.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public interface Context {
+
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/Context2d.java b/user/src/com/google/gwt/canvas/dom/client/Context2d.java
new file mode 100644
index 0000000..f373e17
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/Context2d.java
@@ -0,0 +1,962 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.CanvasElement;
+import com.google.gwt.dom.client.ImageElement;
+
+/**
+ * Rendering interface used to draw on a {@link CanvasElement}.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ * 
+ * @see <a href="http://www.w3.org/TR/2dcontext/#canvasrenderingcontext2d">W3C
+ *      HTML 5 Specification</a>
+ */
+public class Context2d extends JavaScriptObject implements Context {
+  /**
+   * Specifies the context id property used in creating a Context.
+   */
+  public static final String CONTEXT_ID = "2d";
+
+  /**
+   * Enum for the repetition values.
+   * 
+   * @see Context2d#createPattern(ImageElement, Repetition)
+   * @see Context2d#createPattern(CanvasElement, Repetition)
+   */
+  public enum Repetition {
+    REPEAT("repeat"), REPEAT_X("repeat-x"), REPEAT_Y("repeat-y"), NO_REPEAT("no-repeat");
+
+    private final String value;
+
+    private Repetition(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  /**
+   * Enum for line-cap style.
+   * 
+   * @see Context2d#setLineCap(LineCap)
+   */
+  public enum LineCap {
+    BUTT("butt"), ROUND("round"), SQUARE("square");
+
+    private final String value;
+
+    private LineCap(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  /**
+   * Enum for line-join style.
+   * 
+   * @see Context2d#setLineJoin(LineJoin)
+   */
+  public enum LineJoin {
+    ROUND("round"), BEVEL("bevel"), MITER("miter");
+
+    private final String value;
+
+    private LineJoin(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  /**
+   * Enum for composite style.
+   * 
+   * @see Context2d#setGlobalCompositeOperation(Composite)
+   */
+  public enum Composite {
+    /**
+     * A atop B. Display the source image wherever both images are opaque. Display
+     * the destination image wherever the destination image is opaque but the
+     * source image is transparent. Display transparency elsewhere.
+     */
+    SOURCE_ATOP("source-atop"),
+
+    /**
+     * A in B. Display the source image wherever both the source image and
+     * destination image are opaque. Display transparency elsewhere.
+     */
+    SOURCE_IN("source-in"),
+
+    /**
+     * A out B. Display the source image wherever the source image is opaque and
+     * the destination image is transparent. Display transparency elsewhere.
+     */
+    SOURCE_OUT("source-out"),
+
+    /**
+     * A over B. Display the source image wherever the source image is opaque.
+     * Display the destination image elsewhere.
+     */
+    SOURCE_OVER("source-over"),
+
+    /**
+     * B atop A. Same as source-atop but using the destination image instead of
+     * the source image and vice versa.
+     */
+    DESTINATION_ATOP("destination-atop"),
+
+    /**
+     * B in A. Same as source-in but using the destination image instead of the
+     * source image and vice versa.
+     */
+    DESTINATION_IN("destination-in"),
+
+    /**
+     * B out A. Same as source-out but using the destination image instead of the
+     * source image and vice versa.
+     */
+    DESTINATION_OUT("destination-out"),
+
+    /**
+     * B over A. Same as source-over but using the destination image instead of
+     * the source image and vice versa.
+     */
+    DESTINATION_OVER("destination-over"),
+
+    /**
+     * A plus B. Display the sum of the source image and destination image, with
+     * color values approaching 1 as a limit.
+     */
+    LIGHTER("lighter"),
+
+    /**
+     * A (B is ignored). Display the source image instead of the destination
+     * image.
+     */
+    COPY("copy"),
+
+    /**
+     * A xor B. Exclusive OR of the source image and destination image.
+     */
+    XOR("xor");
+
+    private final String value;
+
+    private Composite(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  /**
+   * Enum for text align style.
+   * 
+   * @see Context2d#setTextAlign(TextAlign)
+   */
+  public enum TextAlign {
+    START("start"), END("end"), LEFT("left"), RIGHT("right"), CENTER("center");
+
+    private final String value;
+
+    private TextAlign(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  /**
+   * Enum for text baseline style.
+   * 
+   * @see Context2d#setTextBaseline(TextBaseline)
+   */
+  public enum TextBaseline {
+    TOP("top"), HANGING("hanging"), MIDDLE("middle"), ALPHABETIC("alphabetic"), 
+    IDEOGRAPHIC("ideographic"), BOTTOM("bottom");
+
+    private final String value;
+
+    private TextBaseline(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  protected Context2d() {
+  }
+
+  /**
+   * Draws an arc.
+   */
+  public final native void arc(float x, float y, float radius, float startAngle, 
+      float endAngle) /*-{
+    this.arc(x, y, radius, startAngle, endAngle);
+  }-*/;
+
+  /**
+   * Draws an arc.
+   */
+  public final native void arc(float x, float y, float radius, float startAngle, float endAngle,
+      boolean anticlockwise) /*-{
+    this.arc(x, y, radius, startAngle, endAngle, anticlockwise);
+  }-*/;
+
+  /**
+   * Draws an arc.
+   */
+  public final native void arcTo(float x1, float y1, float x2, float y2, float radius) /*-{
+    this.arcTo(x1, y1, x2, y2, radius);
+  }-*/;
+
+  /**
+   * Begins a new path.
+   */
+  public final native void beginPath() /*-{
+    this.beginPath();
+  }-*/;
+
+  /**
+   * Draws a Bezier curve.
+   */
+  public final native void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x,
+      float y) /*-{
+    this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+  }-*/;
+
+  /**
+   * Clears a rectangle.
+   */
+  public final native void clearRect(float x, float y, float w, float h) /*-{
+    this.clearRect(x, y, w, h);
+  }-*/;
+
+  /**
+   * Creates a new clipping region from the current path.
+   */
+  public final native void clip() /*-{
+    this.clip();
+  }-*/;
+
+  /**
+   * Closes the current path.
+   */
+  public final native void closePath() /*-{
+    this.closePath();
+  }-*/;
+
+  /**
+   * Creates and image data object of the given size.
+   */
+  public final native ImageData createImageData(int w, int h) /*-{
+    return this.createImageData(w, h);
+  }-*/;
+
+  /**
+   * Creates an image data object of the same size as the given object.
+   */
+  public final native ImageData createImageData(ImageData imagedata) /*-{
+    return this.createImageData(imagedata);
+  }-*/;
+
+  /**
+   * Creates a linear gradient.
+   */
+  public final native CanvasGradient createLinearGradient(float x0, float y0, float x1, 
+      float y1) /*-{
+    return this.createLinearGradient(x0, y0, x1, y1);
+  }-*/;
+
+  /**
+   * Creates a pattern from another canvas.
+   */
+  public final CanvasPattern createPattern(CanvasElement image, Repetition repetition) {
+    return createPattern(image, repetition.getValue());
+  }
+
+  /**
+   * Creates a pattern from another canvas.
+   */
+  public final native CanvasPattern createPattern(CanvasElement image, String repetition) /*-{
+    return this.createPattern(image, repetition);
+  }-*/;
+
+  /**
+   * Creates a pattern from an image.
+   */
+  public final CanvasPattern createPattern(ImageElement image, Repetition repetition) {
+    return createPattern(image, repetition.getValue());
+  }
+
+  /**
+   * Creates a pattern from an image.
+   */
+  public final native CanvasPattern createPattern(ImageElement image, String repetition) /*-{
+    return this.createPattern(image, repetition);
+  }-*/;
+
+  /**
+   * Creates a radial gradient.
+   * 
+   * Due to needing to wrap the contained gradient when in devmode, we create an array containing 
+   * the CanvasGradient in devmode (when isScript == false.) @see FillStrokeStyle
+   */
+  public final native CanvasGradient createRadialGradient(float x0, float y0, float r0, float x1,
+      float y1, float r1) /*-{
+    return this.createRadialGradient(x0, y0, r0, x1, y1, r1);
+  }-*/;
+
+  /**
+   * Draws an image.
+   */
+  public final native void drawImage(CanvasElement image, float dx, float dy) /*-{
+    this.drawImage(image, dx, dy);
+  }-*/;
+
+  /**
+   * Draws a scaled image.
+   */
+  public final native void drawImage(CanvasElement image, float dx, float dy, float dw, 
+      float dh) /*-{
+    this.drawImage(image, dx, dy, dw, dh);
+  }-*/;
+
+  /**
+   * Draws a scaled subset of an image.
+   */
+  public final native void drawImage(CanvasElement image, float sx, float sy, float sw, float sh,
+      float dx, float dy, float dw, float dh) /*-{
+    this.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
+  }-*/;
+
+  /**
+   * Draws an image.
+   */
+  public final native void drawImage(ImageElement image, float dx, float dy) /*-{
+    this.drawImage(image, dx, dy);
+  }-*/;
+
+  /**
+   * Draws a scaled image.
+   */
+  public final native void drawImage(ImageElement image, float dx, float dy, float dw, 
+      float dh) /*-{
+    this.drawImage(image, dx, dy, dw, dh);
+  }-*/;
+
+  /**
+   * Draws a scaled subset of an image.
+   */
+  public final native void drawImage(ImageElement image, float sx, float sy, float sw, float sh,
+      float dx, float dy, float dw, float dh) /*-{
+    this.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
+  }-*/;
+
+  /**
+   * Fills the current path.
+   */
+  public final native void fill() /*-{
+    this.fill();
+  }-*/;
+
+  /**
+   * Fills a rectangle.
+   */
+  public final native void fillRect(float x, float y, float w, float h) /*-{
+    this.fillRect(x, y, w, h);
+  }-*/;
+
+  /**
+   * Draw text.
+   */
+  public final native void fillText(String text, float x, float y) /*-{
+    // FF3.0 does not implement this method.
+    if (this.fillText) {
+      this.fillText(text, x, y);
+    }
+  }-*/;
+
+  /**
+   * Draw text squeezed into the given max width.
+   */
+  public final native void fillText(String text, float x, float y, float maxWidth) /*-{
+    this.fillText(text, x, y, maxWidth);
+  }-*/;
+
+  /**
+   * Gets this context's canvas.
+   */
+  public final native CanvasElement getCanvas() /*-{
+    return this.canvas;
+  }-*/;
+
+  /**
+   * Returns the context's fillStyle.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we use different methods
+   * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+   */
+  public final FillStrokeStyle getFillStyle() {
+    if (GWT.isScript()) {
+      return getFillStyleWeb();
+    } else {
+      return getFillStyleDev();
+    }
+  }
+
+  /**
+   * Gets this context's font.
+   */
+  public final native String getFont() /*-{
+    return this.font;
+  }-*/;
+
+  /**
+   * Gets the global alpha value.
+   */
+  public final native float getGlobalAlpha() /*-{
+    return this.globalAlpha;
+  }-*/;
+
+  /**
+   * Gets the global composite operation.
+   */
+  public final native String getGlobalCompositeOperation() /*-{
+    return this.globalCompositeOperation;
+  }-*/;
+
+  /**
+   * Returns an image data object for the screen area denoted by
+   * sx, sy, sw and sh.
+   */
+  public final native ImageData getImageData(float sx, float sy, float sw, float sh) /*-{
+    return this.getImageData(sx, sy, sw, sh);
+  }-*/;
+
+  /**
+   * Gets the current line-cap style.
+   */
+  public final native String getLineCap() /*-{
+    return this.lineCap;
+  }-*/;
+
+  /**
+   * Gets the current line-join style.
+   */
+  public final native String getLineJoin() /*-{
+    return this.lineJoin;
+  }-*/;
+
+  /**
+   * Gets the current line-width.
+   */
+  public final native float getLineWidth() /*-{
+    return this.lineWidth;
+  }-*/;
+
+  /**
+   * Gets the current miter-limit.
+   */
+  public final native float getMiterLimit() /*-{
+    return this.miterLimit;
+  }-*/;
+
+  /**
+   * Gets the current shadow-blur.
+   */
+  public final native float getShadowBlur() /*-{
+    return this.shadowBlur;
+  }-*/;
+
+  /**
+   * Gets the current shadow color.
+   */
+  public final native String getShadowColor() /*-{
+    return this.shadowColor;
+  }-*/;
+
+  /**
+   * Gets the current x-shadow-offset.
+   */
+  public final native float getShadowOffsetX() /*-{
+    return this.shadowOffsetX;
+  }-*/;
+
+  /**
+   * Gets the current y-shadow-offset.
+   */
+  public final native float getShadowOffsetY() /*-{
+    return this.shadowOffsetY;
+  }-*/;
+
+  /**
+   * Returns the context's strokeStyle.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we use different methods
+   * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+   */
+  public final FillStrokeStyle getStrokeStyle() {
+    if (GWT.isScript()) {
+      return getStrokeStyleWeb();
+    } else {
+      return getStrokeStyleDev();
+    }
+  }
+
+  /**
+   * Gets the current text align.
+   */
+  public final native String getTextAlign() /*-{
+    return this.textAlign;
+  }-*/;
+
+  /**
+   * Gets the current text baseline.
+   */
+  public final native String getTextBaseline() /*-{
+    return this.textBaseline;
+  }-*/;
+
+  /**
+   * Returns true if the given point is in the current path.
+   * 
+   * @param x x coordinate of the point to test.
+   * @param y y coordinate of the point to test.
+   * @return true if the given point is in the current path.
+   */
+  public final native boolean isPointInPath(float x, float y) /*-{
+    return this.isPointInPath(x, y);
+  }-*/;
+
+  /**
+   * Draws a line.
+   */
+  public final native void lineTo(float x, float y) /*-{
+    this.lineTo(x, y);
+  }-*/;
+
+  /**
+   * Returns the metrics for the given text.
+   */
+  public final native TextMetrics measureText(String text) /*-{
+    return this.measureText(text);
+  }-*/;
+
+  /**
+   * Sets the current path position.
+   */
+  public final native void moveTo(float x, float y) /*-{
+    this.moveTo(x, y);
+  }-*/;
+
+  /**
+   * Write the given image data to the given screen position.
+   * 
+   * @param imagedata The image data to be written to the screen.
+   * @param x the x-position of the image data.
+   * @param y the y-position of the image data.
+   */
+  public final native void putImageData(ImageData imagedata, float x, float y) /*-{
+    return this.putImageData(imagedata, x, y);
+  }-*/;
+
+  /**
+   * Draws a quadratic curve.
+   */
+  public final native void quadraticCurveTo(float cpx, float cpy, float x, float y) /*-{
+    this.quadraticCurveTo(cpx, cpy, x, y);
+  }-*/;
+
+  /**
+   * Creates a new rectangular path.
+   */
+  public final native void rect(float x, float y, float w, float h) /*-{
+    this.rect(x, y, w, h);
+  }-*/;
+
+  /**
+   * Restores the context's state.
+   */
+  public final native void restore() /*-{
+    this.restore();
+  }-*/;
+
+  /**
+   * Applies rotation to the current transform.
+   */
+  public final native void rotate(float angle) /*-{
+    this.rotate(angle);
+  }-*/;
+
+  /**
+   * Saves the context's state.
+   */
+  public final native void save() /*-{
+    this.save();
+  }-*/;
+
+  /**
+   * Applies scale to the current transform.
+   */
+  public final native void scale(float x, float y) /*-{
+    this.scale(x, y);
+  }-*/;
+
+  /**
+   * Sets the context's fillStyle.
+   *
+   * Due to needing to wrap CssColor when in devmode, we use different methods
+   * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+   * 
+   * @param fillStyle the fill style to set.
+   */
+  public final void setFillStyle(FillStrokeStyle fillStyle) {
+    if (GWT.isScript()) {
+      setFillStyleWeb(fillStyle);
+    } else {
+      setFillStyleDev(fillStyle);
+    }
+  }
+
+  /**
+   * Convenience method to set the context's fillStyle to a CssColor.
+   */
+  public final void setFillStyle(String fillStyleColor) {
+    setFillStyle(CssColor.make(fillStyleColor));
+  }
+
+  /**
+   * Sets the font.
+   */
+  public final native void setFont(String f) /*-{
+    this.font = f;
+  }-*/;
+
+  /**
+   * Sets the global alpha value.
+   */
+  public final native void setGlobalAlpha(float alpha) /*-{
+    this.globalAlpha = alpha;
+  }-*/;
+
+  /**
+   * Sets the global composite operation.
+   */
+  public final void setGlobalCompositeOperation(Composite composite) {
+    setGlobalCompositeOperation(composite.getValue());
+  }
+
+  /**
+   * Sets the global composite operation.
+   */
+  public final native void setGlobalCompositeOperation(String globalCompositeOperation) /*-{
+    this.globalCompositeOperation = globalCompositeOperation;
+  }-*/;
+
+  /**
+   * Sets the line-cap style.
+   */
+  public final void setLineCap(LineCap lineCap) {
+    setLineCap(lineCap.getValue());
+  }
+
+  /**
+   * Sets the line-cap style.
+   */
+  public final native void setLineCap(String lineCap) /*-{
+    this.lineCap = lineCap;
+  }-*/;
+
+  /**
+   * Sets the line-join style.
+   */
+  public final void setLineJoin(LineJoin lineJoin) {
+    setLineJoin(lineJoin.getValue());
+  }
+
+  /**
+   * Sets the line-join style.
+   */
+  public final native void setLineJoin(String lineJoin) /*-{
+    this.lineJoin = lineJoin;
+  }-*/;
+
+  /**
+   * Sets the line-width.
+   */
+  public final native void setLineWidth(float lineWidth) /*-{
+    this.lineWidth = lineWidth;
+  }-*/;
+
+  /**
+   * Sets the miter-limit.
+   */
+  public final native void setMiterLimit(float miterLimit) /*-{
+    this.miterLimit = miterLimit;
+  }-*/;
+
+  /**
+   * Sets the shadow-blur.
+   */
+  public final native void setShadowBlur(float shadowBlur) /*-{
+    this.shadowBlur = shadowBlur;
+  }-*/;
+
+  /**
+   * Sets the shadow-color.
+   */
+  public final native void setShadowColor(String shadowColor) /*-{
+    this.shadowColor = shadowColor;
+  }-*/;
+
+  /**
+   * Sets the x-shadow-offset.
+   */
+  public final native void setShadowOffsetX(float shadowOffsetX) /*-{
+    this.shadowOffsetX = shadowOffsetX;
+  }-*/;
+
+  /**
+   * Sets the y-shadow-offset.
+   */
+  public final native void setShadowOffsetY(float shadowOffsetY) /*-{
+    this.shadowOffsetY = shadowOffsetY;
+  }-*/;
+
+  /**
+   * Convenience method to set the context's strokeStyle to a CssColor.
+   */
+  public final void setStrokeStyle(String strokeStyleColor) {
+    setStrokeStyle(CssColor.make(strokeStyleColor));
+  }
+
+  /**
+   * Sets the context's strokeStyle.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we use different methods
+   * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+   * 
+   * @param strokeStyle the stroke style to set.
+   */
+  public final void setStrokeStyle(FillStrokeStyle strokeStyle) {
+    if (GWT.isScript()) {
+      setStrokeStyleWeb(strokeStyle);
+    } else {
+      setStrokeStyleDev(strokeStyle);
+    }
+  }
+
+  /** 
+   * Sets the text alignment.
+   */
+  public final void setTextAlign(TextAlign align) {
+    setTextAlign(align.getValue());
+  }
+
+  /** 
+   * Sets the text alignment.
+   */
+  public final native void setTextAlign(String align) /*-{
+    this.textAlign = align
+  }-*/;
+
+  /**
+   * Sets the text baseline.
+   */
+  public final native void setTextBaseline(String baseline) /*-{
+    this.textBaseline = baseline
+  }-*/;
+
+  /**
+   * Sets the text baseline.
+   */
+  public final void setTextBaseline(TextBaseline baseline) {
+    setTextBaseline(baseline.getValue());
+  }
+
+  /**
+   * Sets the 2D transformation matrix.
+   */
+  public final native void setTransform(float m11, float m12, float m21, float m22, float dx,
+      float dy) /*-{
+    this.setTransform(m11, m12, m21, m22, dx, dy);
+  }-*/;
+
+  /**
+   * Draws the current path with the current stroke style.
+   */
+  public final native void stroke() /*-{
+    this.stroke();
+  }-*/;
+
+  /**
+   * Draws a rectangle with the current stroke style.
+   */
+  public final native void strokeRect(float x, float y, float w, float h) /*-{
+    this.strokeRect(x, y, w, h);
+  }-*/;
+
+  /**
+   * Draws the text outline.
+   */
+  public final native void strokeText(String text, float x, float y) /*-{
+    this.strokeText(text, x, y);
+  }-*/;
+
+  /**
+   * Draws the text outline, squeezing the text into the given max width by compressing the font.
+   */
+  public final native void strokeText(String text, float x, float y, float maxWith) /*-{
+    this.strokeText(text, x, y, maxWidth);
+  }-*/;
+
+  /**
+   * Multiplies the current transform by the given matrix.
+   */
+  public final native void transform(float m11, float m12, float m21, float m22, float dx, 
+      float dy) /*-{
+    this.transform(m11, m12, m21, m22, dx, dy);
+  }-*/;
+
+  /**
+   * Applies translation to the current transform.
+   */
+  public final native void translate(float x, float y) /*-{
+    this.translate(x, y);
+  }-*/;
+
+  /**
+   * Returns the fill style when in devmode.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we wrap the CssColor in an array when in
+   * devmode (when isScript == false) or not. @see CssColor
+   * 
+   * @return the fill style.
+   */
+  final native FillStrokeStyle getFillStyleDev() /*-{
+    if (typeof(this.fillStyle) == 'string') { // if it's a color
+      return [this.fillStyle];
+    } else {
+      return this.fillStyle;
+    }
+  }-*/;
+
+  /**
+   * Returns the fill style when in webmode.
+   * 
+   * @return the fill style.
+   */
+  final native FillStrokeStyle getFillStyleWeb() /*-{
+    return this.fillStyle;
+  }-*/;
+
+  /**
+   * Returns the stroke style when in devmode.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we wrap the CssColor in an array when in
+   * devmode (when isScript == false) or not. @see CssColor
+   * 
+   * @return the stroke style.
+   */
+  final native FillStrokeStyle getStrokeStyleDev() /*-{
+    if (typeof(this.strokeStyle) == 'string') { // if it's a color
+      return [this.strokeStyle];
+    } else {
+      return this.strokeStyle;
+    }
+  }-*/;
+
+  /**
+   * Returns the stroke style when in webmode.
+   * 
+   * @return the stroke style.
+   */
+  final native FillStrokeStyle getStrokeStyleWeb() /*-{
+    return this.strokeStyle;
+  }-*/;
+
+  /**
+   * Sets the fill style when in devmode.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we check whether the fillStyle is a wrapped
+   * array and unwrap if necessary. @see CssColor
+   * 
+   * @param fillStyle the fill style to set.
+   */
+  final native void setFillStyleDev(FillStrokeStyle fillStyle) /*-{
+    if (fillStyle[0] && typeof(fillStyle[0]) == 'string') {
+      this.fillStyle = fillStyle[0];
+    } else {
+      this.fillStyle = fillStyle;
+    }
+  }-*/;
+
+  /**
+   * Sets the fill style when in webmode.
+   * 
+   * @param fillStyle the fill style to set.
+   */
+  final native void setFillStyleWeb(FillStrokeStyle fillStyle) /*-{
+    this.fillStyle = fillStyle;
+  }-*/;
+
+  /**
+   * Sets the stroke style when in devmode.
+   * 
+   * Due to needing to wrap CssColor when in devmode, we check whether the strokeStyle is a wrapped
+   * array and unwrap if necessary. @see CssColor
+   * 
+   * @param strokeStyle the stroke style to set.
+   */
+  final native void setStrokeStyleDev(FillStrokeStyle strokeStyle) /*-{
+    if (strokeStyle[0] && typeof(strokeStyle[0]) == 'string') {
+      this.strokeStyle = strokeStyle[0];
+    } else {
+      this.strokeStyle = strokeStyle;
+    }
+  }-*/;
+
+  /**
+   * Sets the stroke style when in webmode.
+   * 
+   * @param strokeStyle the strokeStyle to set.
+   */
+  final native void setStrokeStyleWeb(FillStrokeStyle strokeStyle) /*-{
+    this.strokeStyle = strokeStyle;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/CssColor.java b/user/src/com/google/gwt/canvas/dom/client/CssColor.java
new file mode 100644
index 0000000..d59fab8
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CssColor.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * CSS Color object.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ * 
+ * <p>
+ * To handle devmode we must wrap JSO strings in an array. Therefore, when in devmode, CssColor is
+ * actually an array with one element that is the JSO. In webmode, this is not needed.
+ * </p>
+ * 
+ * @see <a href="http://www.w3.org/TR/CSS1/#color">Cascading Style Sheets, level 1</a>
+ */
+public class CssColor extends FillStrokeStyle {
+
+  /**
+   * Sets the RGB color value.
+   * 
+   * @param r red, int between 0 and 255
+   * @param g green, int between 0 and 255
+   * @param b blue, int between 0 and 255
+   */
+  public static final CssColor make(int r, int g, int b) {
+    return make("rgb(" + r + "," + g + "," + b + ")");
+  }
+
+  /**
+   * Creates a CssColor object.
+   * 
+   * Examples: blue, #ff0000, #f00, rgb(255,0,0)
+   * 
+   * @param cssColor the CSS color
+   */
+  public static final native CssColor make(String cssColor) /*-{
+    // Due to needing to wrap CssColor when in devmode, we check whether the we are in devmode and
+    // wrap the String in an array if necessary.
+    return @com.google.gwt.core.client.GWT::isScript()() ? cssColor : [cssColor];
+  }-*/;
+
+  protected CssColor() {
+  }
+
+  /**
+   * Returns the value of the CssColor, as a String.
+   * 
+   * @return the value of the color, as a String.
+   */
+  public final native String value() /*-{
+    // Due to needing to wrap CssColor when in devmode, we check whether the we are in devmode and
+    // unwrap the String if necessary.
+    return @com.google.gwt.core.client.GWT::isScript()() ? this : this[0];
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/FillStrokeStyle.java b/user/src/com/google/gwt/canvas/dom/client/FillStrokeStyle.java
new file mode 100644
index 0000000..b1cc31a
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/FillStrokeStyle.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Represents a CssColor, a CanvasGradient, or a CanvasPattern that is used for stroke and fill.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class FillStrokeStyle extends JavaScriptObject {
+  public static final int TYPE_CSSCOLOR = 0, TYPE_GRADIENT = 1, TYPE_PATTERN = 2;
+
+  protected FillStrokeStyle() { }
+
+  /**
+   * Returns the type of this FillStrokeStyle as an integer.
+   * 
+   * @return The type of the object.
+   */
+  public final native int getType() /*-{
+    // Due to needing to wrap the contained CssColor when in devmode, we must unwrap the color to
+    // check its type when in devmode (when isScript == false.) @see CssColor
+    var unwrappedColor = @com.google.gwt.core.client.GWT::isScript()() ? this : this[0];
+    if (unwrappedColor && typeof(unwrappedColor) == 'string') {
+      return @com.google.gwt.canvas.dom.client.FillStrokeStyle::TYPE_CSSCOLOR;
+    } else if (typeof(this.addColorStop) == 'function') {
+      return @com.google.gwt.canvas.dom.client.FillStrokeStyle::TYPE_GRADIENT;
+    } else {
+      return @com.google.gwt.canvas.dom.client.FillStrokeStyle::TYPE_PATTERN;
+    }
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/ImageData.java b/user/src/com/google/gwt/canvas/dom/client/ImageData.java
new file mode 100644
index 0000000..d3bec67
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/ImageData.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Object that holds image data and a size. 
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class ImageData extends JavaScriptObject {
+  /**
+   * Number of colors at each location in the array.
+   * 
+   * Because image data is stored as RGBA, this is 4.
+   */
+  @SuppressWarnings("unused")
+  private static final int NUM_COLORS = 4;
+
+  /**
+   * Offsets for each color use RGBA ordering.
+   */
+  private static final int OFFSET_RED = 0;
+  private static final int OFFSET_GREEN = 1;
+  private static final int OFFSET_BLUE = 2;
+  private static final int OFFSET_ALPHA = 3;
+
+  protected ImageData() {
+  }
+
+  /**
+   * Returns the alpha value at position (x,y).
+   * 
+   * @param x the x coordinate
+   * @param y the y coordinate
+   * @return the alpha value at position (x,y), or 0 if not in the image.
+   */
+  public final int getAlphaAt(int x, int y) {
+    return getColorAt(x, y, OFFSET_ALPHA);
+  }
+
+  /**
+   * Returns the blue value at position (x,y).
+   * 
+   * @param x the x coordinate
+   * @param y the y coordinate
+   * @return the blue value at position (x,y), or 0 if not in the image.
+   */
+  public final int getBlueAt(int x, int y) {
+    return getColorAt(x, y, OFFSET_BLUE);
+  }
+
+  /**
+   * Returns a canvas pixel array of the size width * height * 4.
+   */
+  public final native CanvasPixelArray getData() /*-{
+    return this.data;
+  }-*/;
+
+  /**
+   * Returns the green value at position (x,y).
+   * 
+   * @param x the x coordinate
+   * @param y the y coordinate
+   * @return the green value at position (x,y), or 0 if not in the image.
+   */
+  public final int getGreenAt(int x, int y) {
+    return getColorAt(x, y, OFFSET_GREEN);
+  }
+
+  /**
+   * Returns the height of this image data object.
+   */
+  public final native int getHeight() /*-{
+    return this.height;
+  }-*/;
+
+  /**
+   * Returns the red value at position (x,y).
+   * 
+   * @param x the x coordinate
+   * @param y the y coordinate
+   * @return the red value at position (x,y), or 0 if not in the image.
+   */
+  public final int getRedAt(int x, int y) {
+    return getColorAt(x, y, OFFSET_RED);
+  }
+
+  /**
+   * Returns the width of this image data object.
+   */
+  public final native int getWidth() /*-{
+    return this.width;
+  }-*/;
+
+  /**
+   * Sets the alpha value at position (x,y).
+   * 
+   * @param alpha the alpha value
+   * @param x the x coordinate
+   * @param y the y coordinate
+   */
+  public final void setAlphaAt(int alpha, int x, int y) {
+    setColorAt(alpha, x, y, OFFSET_ALPHA);
+  }
+
+  /**
+   * Sets the blue value at position (x,y).
+   * 
+   * @param blue the blue value
+   * @param x the x coordinate
+   * @param y the y coordinate
+   */
+  public final void setBlueAt(int blue, int x, int y) {
+    setColorAt(blue, x, y, OFFSET_BLUE);
+  }
+
+  /**
+   * Sets the green value at position (x,y).
+   * 
+   * @param green the green value
+   * @param x the x coordinate
+   * @param y the y coordinate
+   */
+  public final void setGreenAt(int green, int x, int y) {
+    setColorAt(green, x, y, OFFSET_GREEN);
+  }
+
+  /**
+   * Sets the red value at position (x,y).
+   * 
+   * @param red the red value
+   * @param x the x coordinate
+   * @param y the y coordinate
+   */
+  public final void setRedAt(int red, int x, int y) {
+    setColorAt(red, x, y, OFFSET_RED);
+  }
+
+  /**
+   * Returns the color value at position (x,y) with the specified offset.
+   * 
+   * Colors are stored in RGBA format, where the offset determines the color (R, G, B, or A.) The
+   * values are stored in row-major order. If the specified location is not in the image, 0 is
+   * returned.
+   * 
+   * @param x the x coordinate
+   * @param y the y coordinate
+   * @param offset the color offset
+   * @return the color value at position (x,y), or 0 if not in the image.
+   */
+  private native int getColorAt(int x, int y, int offset) /*-{
+    return this.data[@com.google.gwt.canvas.dom.client.ImageData::NUM_COLORS * 
+      (x + y * this.width) + offset] || 0;
+  }-*/;
+
+  /**
+   * Sets the color value at position (x,y) with the specified offset.
+   * 
+   * Colors are stored in RGBA format, where the offset determines the color (R, G, B, or A.) The
+   * values are stored in row-major order.
+   * 
+   * @param color the color (in the range 0...255)
+   * @param x the x coordinate
+   * @param y the y coordinate
+   * @param offset the color offset
+   */
+  private native void setColorAt(int color, int x, int y, int offset) /*-{
+    this.data[@com.google.gwt.canvas.dom.client.ImageData::NUM_COLORS * 
+      (x + y * this.width) + offset] = color;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/TextMetrics.java b/user/src/com/google/gwt/canvas/dom/client/TextMetrics.java
new file mode 100644
index 0000000..0e21efc
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/TextMetrics.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * HTML 5 Canvas text metrics.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class TextMetrics extends JavaScriptObject {
+
+  protected TextMetrics() {
+  }
+
+  /**
+   * Return the width of the rendered text.
+   * 
+   * @return the width of the text
+   */
+  public final native float getWidth() /*-{
+    return this.width;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/dom/client/CanvasElement.java b/user/src/com/google/gwt/dom/client/CanvasElement.java
new file mode 100644
index 0000000..ece13ba
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/CanvasElement.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dom.client;
+
+import com.google.gwt.canvas.dom.client.Context;
+import com.google.gwt.canvas.dom.client.Context2d;
+
+/**
+ * Canvas element.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ * 
+ * @see <a href="http://www.w3.org/TR/html5/#canvas">W3C HTML 5 Specification</a>
+ */
+@TagName(CanvasElement.TAG)
+public class CanvasElement extends Element {
+
+  public static final String TAG = "canvas";
+
+  protected CanvasElement() {
+  }
+
+  /**
+   * Gets the rendering context that may be used to draw on this canvas.
+   * 
+   * @return the canvas rendering context
+   */
+  public final native Context getContext(String contextId) /*-{
+    return this.getContext(contextId);
+  }-*/;
+
+  /**
+   * Returns a 2D rendering context.
+   * 
+   * This is a convenience method, @see {@link #getContext(String)}
+   * 
+   * @return a 2D canvas rendering context
+   */
+  public final native Context2d getContext2d() /*-{
+    return this.getContext(@com.google.gwt.canvas.dom.client.Context2d::CONTEXT_ID);
+  }-*/;
+
+  /**
+   * Gets the height of the canvas.
+   * 
+   * @return the height, in pixels
+   */
+  public final native int getHeight() /*-{
+    return this.height;
+  }-*/;
+
+  /**
+   * Gets the width of the canvas.
+   * 
+   * @return the width, in pixels
+   */
+  public final native int getWidth() /*-{
+    return this.width;
+  }-*/;
+
+  /**
+   * Sets the height of the canvas.
+   * 
+   * @param height the height, in pixels
+   */
+  public final native void setHeight(int height) /*-{
+    this.height = height;
+  }-*/;
+
+  /**
+   * Sets the width of the canvas.
+   * 
+   * @param width the width, in pixels
+   */
+  public final native void setWidth(int width) /*-{
+    this.width = width;
+  }-*/;
+
+  /**
+   * Returns a data URL for the current content of the canvas element.
+   * 
+   * @return a data URL for the current content of this element.
+   */
+  public final native String toDataUrl() /*-{
+    return this.toDataURL();
+  }-*/;
+
+  /**
+   * Returns a data URL for the current content of the canvas element, with a specified type.
+   * 
+   * @param type the type of the data url, e.g., image/jpeg or image/png.
+   * @return a data URL for the current content of this element with the specified type.
+   */
+  public final native String toDataUrl(String type) /*-{
+    return this.toDataURL(type);
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
index 584e9e3..e46f3ca 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
@@ -19,6 +19,39 @@
  * Mozilla implementation of StandardBrowser.
  */
 class DOMImplMozilla extends DOMImplStandard {
+  private static boolean isGecko190OrBefore;
+  private static boolean isGecko190OrBeforeDetected;
+
+  /**
+   * Return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+   * 
+   * @return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+   */
+  @SuppressWarnings("unused")
+  static boolean isGecko190OrBefore() {
+    if (!isGecko190OrBeforeDetected) {
+      isGecko190OrBefore = isGecko190OrBeforeImpl();
+      isGecko190OrBeforeDetected = true;
+    }
+    return isGecko190OrBefore;
+  }
+
+  /**
+   * Return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+   * 
+   * @return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+   */
+  private static native boolean isGecko190OrBeforeImpl() /*-{
+    var result = /rv:([0-9]+)\.([0-9]+)\.([0-9]+)?/.exec(navigator.userAgent.toLowerCase());
+    if (result && result.length >= 3) {
+      var version = (parseInt(result[1]) * 1000000) + (parseInt(result[2]) * 1000) + 
+        parseInt(result.length == 4 ? result[3] : 0);
+      if (version <= 1009000) {
+        return true;
+      }
+    }
+    return false;
+  }-*/;
 
   @Override
   public native void buttonClick(ButtonElement button) /*-{
@@ -204,6 +237,11 @@
     }
   }-*/;
 
+  /**
+   * Return true if using Gecko 1.9 (Firefox 3) or later.
+   * 
+   * @return true if using Gecko 1.9 (Firefox 3) or later
+   */
   private native boolean isGecko19() /*-{
     var result = /rv:([0-9]+)\.([0-9]+)/.exec(navigator.userAgent.toLowerCase());
     if (result && result.length == 3) {
diff --git a/user/src/com/google/gwt/dom/client/Document.java b/user/src/com/google/gwt/dom/client/Document.java
index c193a26..3aaf978 100644
--- a/user/src/com/google/gwt/dom/client/Document.java
+++ b/user/src/com/google/gwt/dom/client/Document.java
@@ -114,6 +114,15 @@
   }
 
   /**
+   * Creates an &lt;canvas&gt; element.
+   * 
+   * @return the newly created element
+   */
+  public final CanvasElement createCanvasElement() {
+    return (CanvasElement) DOMImpl.impl.createElement(this, CanvasElement.TAG);
+  }
+
+  /**
    * Creates a &lt;caption&gt; element.
    * 
    * @return the newly created element
diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml
index 28d9d20..905c37e 100644
--- a/user/src/com/google/gwt/user/User.gwt.xml
+++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -18,6 +18,7 @@
 <!-- Most new code should inherit this module.                              -->
 <!--                                                                        -->
 <module>
+   <inherits name="com.google.gwt.canvas.Canvas"/>
    <inherits name="com.google.gwt.core.Core"/>
    <inherits name="com.google.gwt.text.Text"/>
    <inherits name="com.google.gwt.event.Event"/>
diff --git a/user/src/com/google/gwt/user/client/IsSupported.java b/user/src/com/google/gwt/user/client/IsSupported.java
new file mode 100644
index 0000000..4a883ff
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/IsSupported.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.user.client;
+
+/**
+ * Interface for classes that are only supported on a limited set of browsers.
+ * 
+ * By convention, classes that implement IsSupported will provide a static method
+ * <code>boolean isSupported()</code> that checks whether the feature is supported at runtime.
+ */
+public interface IsSupported {
+
+}
diff --git a/user/test/com/google/gwt/canvas/CanvasSuite.java b/user/test/com/google/gwt/canvas/CanvasSuite.java
new file mode 100644
index 0000000..0518df2
--- /dev/null
+++ b/user/test/com/google/gwt/canvas/CanvasSuite.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.canvas;
+
+import com.google.gwt.canvas.client.CanvasTest;
+import com.google.gwt.canvas.dom.client.Context2dTest;
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * Tests of Canvas.
+ */
+public class CanvasSuite {
+  public static Test suite() {
+    GWTTestSuite suite = new GWTTestSuite("Test suite for Canvas GWTTestCases");
+    
+    suite.addTestSuite(CanvasTest.class);
+    suite.addTestSuite(Context2dTest.class);
+    
+    return suite;
+  }
+  
+  private CanvasSuite() {
+  }
+}
diff --git a/user/test/com/google/gwt/canvas/client/CanvasTest.java b/user/test/com/google/gwt/canvas/client/CanvasTest.java
new file mode 100644
index 0000000..bd3cb77
--- /dev/null
+++ b/user/test/com/google/gwt/canvas/client/CanvasTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.client;
+
+import com.google.gwt.canvas.dom.client.Context2d;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Tests {@link Canvas}.
+ * 
+ * Because HtmlUnit does not support HTML5, you will need to run these tests manually in order to
+ * have them run. To do that, go to "run configurations" or "debug configurations", select the test
+ * you would like to run, and put this line in the VM args under the arguments tab:
+ * -Dgwt.args="-runStyle Manual:1"
+ */
+@DoNotRunWith(Platform.HtmlUnitUnknown)
+public class CanvasTest extends GWTTestCase {
+  protected Canvas canvas1;
+  protected Canvas canvas2;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.canvas.Canvas";
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    canvas1 = new Canvas();
+    canvas2 = new Canvas();
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    RootPanel.get().add(canvas1);
+    RootPanel.get().add(canvas2);
+  }
+
+  @Override
+  protected void gwtTearDown() throws Exception {
+    RootPanel.get().remove(canvas1);
+    RootPanel.get().remove(canvas2);
+  }
+
+  /*
+   * If the canvas has no pixels (i.e. either its horizontal dimension or its vertical dimension 
+   * is zero) then the method must return the string "data:,". (This is the shortest data: URL; 
+   * it represents the empty string in a text/plain resource.)
+   * 
+   * Due to browser inconsistencies, we just check for data:something.
+   */
+  public void testBlankDataUrl() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("0px");
+    canvas1.setWidth("0px");
+    assertEquals(0, canvas1.getOffsetHeight());
+    assertEquals(0, canvas1.getOffsetWidth());
+    canvas1.setCoordinateSpaceHeight(0);
+    canvas1.setCoordinateSpaceWidth(0);
+    
+    String dataUrl = canvas1.toDataUrl();
+    assertTrue("toDataURL() should return data:something", dataUrl.startsWith("data:"));
+  }
+
+  public void testHeightAndWidth() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    assertEquals(40, canvas1.getOffsetHeight());
+    assertEquals(60, canvas1.getOffsetWidth());
+
+    // resize
+    canvas1.setHeight("41px");
+    canvas1.setWidth("61px");
+    assertEquals(41, canvas1.getOffsetHeight());
+    assertEquals(61, canvas1.getOffsetWidth());
+
+    // add 2d context, resize internal size, should have no effect
+    Context2d context = canvas1.getContext2d();
+    canvas1.setCoordinateSpaceHeight(140);
+    canvas1.setCoordinateSpaceWidth(160);
+    context.fillRect(2, 2, 300, 300);
+    
+    assertEquals(41, canvas1.getOffsetHeight());
+    assertEquals(61, canvas1.getOffsetWidth());
+  }
+
+  public void testInternalHeightAndWidth() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    assertEquals(40, canvas1.getOffsetHeight());
+    assertEquals(60, canvas1.getOffsetWidth());
+
+    // resize internal
+    canvas1.setCoordinateSpaceHeight(140);
+    canvas1.setCoordinateSpaceWidth(160);
+    assertEquals(140, canvas1.getCoordinateSpaceHeight());
+    assertEquals(160, canvas1.getCoordinateSpaceWidth());
+
+    // resize element, should have no effect on internal size
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    assertEquals(40, canvas1.getOffsetHeight());
+    assertEquals(60, canvas1.getOffsetWidth());
+    assertEquals(140, canvas1.getCoordinateSpaceHeight());
+    assertEquals(160, canvas1.getCoordinateSpaceWidth());
+    
+    // resize internal
+    canvas1.setCoordinateSpaceHeight(141);
+    canvas1.setCoordinateSpaceWidth(161);
+    assertEquals(141, canvas1.getCoordinateSpaceHeight());
+    assertEquals(161, canvas1.getCoordinateSpaceWidth());
+  }
+
+  public void testDataUrlWithType() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("10px");
+    canvas1.setWidth("10px");
+    canvas1.setCoordinateSpaceHeight(10);
+    canvas1.setCoordinateSpaceWidth(10);
+    
+    String dataUrl = canvas1.toDataUrl("image/png");
+    assertTrue("toDataURL(image/png) should return data:image/png[data]", 
+        dataUrl.startsWith("data:image/png"));
+  }
+}
diff --git a/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
new file mode 100644
index 0000000..f7834ce
--- /dev/null
+++ b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.canvas.client.Canvas;
+import com.google.gwt.canvas.dom.client.Context2d.Composite;
+import com.google.gwt.canvas.dom.client.Context2d.LineCap;
+import com.google.gwt.canvas.dom.client.Context2d.LineJoin;
+import com.google.gwt.canvas.dom.client.Context2d.TextAlign;
+import com.google.gwt.canvas.dom.client.Context2d.TextBaseline;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Tests {@link Context2d}.
+ * 
+ * Because HtmlUnit does not support HTML5, you will need to run these tests manually in order to
+ * have them run. To do that, go to "run configurations" or "debug configurations", select the test
+ * you would like to run, and put this line in the VM args under the arguments tab:
+ * -Dgwt.args="-runStyle Manual:1"
+ */
+@DoNotRunWith(Platform.HtmlUnitUnknown)
+public class Context2dTest extends GWTTestCase {
+  protected Canvas canvas1;
+  protected Canvas canvas2;
+
+  native boolean isGecko190OrBefore() /*-{
+    return @com.google.gwt.dom.client.DOMImplMozilla::isGecko190OrBeforeImpl()();
+  }-*/;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.canvas.Canvas";
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    canvas1 = new Canvas();
+    canvas2 = new Canvas();
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    RootPanel.get().add(canvas1);
+    RootPanel.get().add(canvas2);
+  }
+
+  @Override
+  protected void gwtTearDown() throws Exception {
+    RootPanel.get().remove(canvas1);
+    RootPanel.get().remove(canvas2);
+  }
+
+  public void testFillRect() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(80);
+    canvas1.setCoordinateSpaceWidth(120);
+    assertEquals(40, canvas1.getOffsetHeight());
+    assertEquals(60, canvas1.getOffsetWidth());
+    assertEquals(80, canvas1.getCoordinateSpaceHeight());
+    assertEquals(120, canvas1.getCoordinateSpaceWidth());
+
+    // get a 2d context
+    Context2d context = canvas1.getContext2d();
+
+    // draw green rectangle filling 1st half of canvas
+    context.setFillStyle(CssColor.make("#00fF00"));
+    context.fillRect(0f, 0f, 60f, 80f);
+    // draw red rectangle filling 2nd half of canvas
+    context.setFillStyle("#fF0000");
+    context.fillRect(60f, 0f, 60f, 80f);
+
+    // test that the first pixel is green and the last pixel is red
+    ImageData imageData = context.getImageData(0, 0, 120, 80);
+    CanvasPixelArray pixelArray = imageData.getData();
+    assertEquals(000, pixelArray.get(0));
+    assertEquals(255, pixelArray.get(1));
+    assertEquals(000, pixelArray.get(2));
+    assertEquals(255, pixelArray.get(3));
+    assertEquals(255, pixelArray.get((40 * 20 - 1) * 4 + 0));
+    assertEquals(000, pixelArray.get((40 * 20 - 1) * 4 + 1));
+    assertEquals(000, pixelArray.get((40 * 20 - 1) * 4 + 2));
+    assertEquals(255, pixelArray.get((40 * 20 - 1) * 4 + 3));
+    
+    // draw a blue square in the top left
+    context.setFillStyle("#0000fF");
+    context.fillRect(0f, 0f, 20f, 20f);
+    imageData = context.getImageData(0, 0, 20, 20);
+    pixelArray = imageData.getData();
+    
+    // test that the first pixel is blue
+    assertEquals(000, pixelArray.get(0));
+    assertEquals(000, pixelArray.get(1));
+    assertEquals(255, pixelArray.get(2));
+    assertEquals(255, pixelArray.get(3));
+  }
+
+  public void testFillStyle() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    // get the 2d contexts
+    Context2d context1 = canvas1.getContext2d();
+    Context2d context2 = canvas2.getContext2d();
+
+    // test that a color can be set and is correct
+    context1.setFillStyle(CssColor.make("#ffff00"));
+    FillStrokeStyle fillStyleCol = context1.getFillStyle();
+    assertTrue("fillStyleCol is a color", fillStyleCol.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertFalse("fillStyleCol is a color", fillStyleCol.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertFalse("fillStyleCol is a color", fillStyleCol.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+    // test that a gradient can be set and is correct
+    CanvasGradient gradient = context1.createLinearGradient(0, 0, 10, 20);
+    gradient.addColorStop(0.5f, "#ffaaff");
+    context1.setFillStyle(gradient);
+    FillStrokeStyle fillStyleGrad = context1.getFillStyle();
+    assertFalse("fillStyleGrad is a gradient", fillStyleGrad.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertTrue("fillStyleGrad is a gradient", fillStyleGrad.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertFalse("fillStyleGrad is a gradient", fillStyleGrad.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+    // test that a pattern can be set and is correct
+    CanvasPattern pattern = context1.createPattern(canvas1.getCanvasElement(), 
+        Context2d.Repetition.REPEAT);
+    context1.setFillStyle(pattern);
+    FillStrokeStyle fillStylePat = context1.getFillStyle();
+    assertFalse("fillStylePat is a pattern", fillStylePat.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertFalse("fillStylePat is a pattern", fillStylePat.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertTrue("fillStylePat is a pattern", fillStylePat.getType() == FillStrokeStyle.TYPE_PATTERN);
+    
+    // test that a StrokeStyle can be passed around w/o knowing what it is
+    FillStrokeStyle fillStyle = context1.getFillStyle();
+    context2.setFillStyle(fillStyle);
+    FillStrokeStyle fillStyle2 = context2.getFillStyle();
+    assertFalse("fillStyle2 is a pattern", fillStyle2.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertFalse("fillStyle2 is a pattern", fillStyle2.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertTrue("fillStyle2 is a pattern", fillStyle2.getType() == FillStrokeStyle.TYPE_PATTERN);
+  }
+
+  public void testFont() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    Context2d context = canvas1.getContext2d();
+    context.setFont("40px Times New Roman");
+    assertEquals("40px Times New Roman", context.getFont());
+  }
+
+  public void testGlobalAlpha() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    Context2d context = canvas1.getContext2d();
+    context.setGlobalAlpha(0.5f);
+    assertEquals(0.5f, context.getGlobalAlpha());
+  }
+
+  // This test currently fails on IE9 and is disabled.
+  public void disabled_testGlobalComposite() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    Context2d context = canvas1.getContext2d();
+    context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
+    assertEquals(Composite.SOURCE_OVER.getValue(), context.getGlobalCompositeOperation());
+    context.setGlobalCompositeOperation(Composite.DESTINATION_OVER);
+    assertEquals(Composite.DESTINATION_OVER.getValue(), context.getGlobalCompositeOperation());
+  }
+
+  public void testGradient() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(40);
+    canvas1.setCoordinateSpaceWidth(60);
+    Context2d context = canvas1.getContext2d();
+
+    // fill the canvas with black
+    context.setFillStyle("#000000");
+    context.fillRect(0, 0, 60, 40);
+    
+    // create a linear gradient from the top-left to the bottom-right.
+    CanvasGradient linearGradient = context.createLinearGradient(0, 0, 60, 40);
+    linearGradient.addColorStop(0, "#ff0000");
+    linearGradient.addColorStop(1, "#00ffff");
+    context.setFillStyle(linearGradient);
+    context.fillRect(0, 0, 60, 40);
+    
+    // test that the first pixel is ff0000, the last is 00ffff, and the middle is something else
+    // isn't exact due to rounding, give 5px approx wiggle-room
+    int approx = 5;
+    ImageData imageData = context.getImageData(0, 0, 60, 40);
+    CanvasPixelArray pixelArray = imageData.getData();
+
+    assertTrue(255 - pixelArray.get(0) < approx);
+    assertTrue(pixelArray.get(1) - 000 < approx);
+    assertTrue(pixelArray.get(2) - 000 < approx);
+    assertTrue(255 - pixelArray.get(3) < approx);
+    assertFalse(000 == pixelArray.get((60 * 40 / 2) * 4 + 0));
+    assertFalse(255 == pixelArray.get((60 * 40 / 2) * 4 + 0));
+    assertTrue(pixelArray.get((60 * 40 - 1) * 4 + 0) - 000 < approx);
+    assertTrue(255 - pixelArray.get((60 * 40 - 1) * 4 + 1) < approx);
+    assertTrue(255 - pixelArray.get((60 * 40 - 1) * 4 + 2) < approx);
+    assertTrue(255 - pixelArray.get((60 * 40 - 1) * 4 + 3) < approx);
+  }
+
+  public void testImageData() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+    
+    // Firefox 3.0 does not support createImageData(), so the following tests are disabled
+    // for FF 3.0 and before.
+    if (isGecko190OrBefore()) {
+      return;
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(40);
+    canvas1.setCoordinateSpaceWidth(60);
+    Context2d context = canvas1.getContext2d();
+
+    // fill the canvas with ffff00
+    context.setFillStyle("#ffff00");
+    context.fillRect(0, 0, 60, 40);
+    
+    // create a 1 x 1 image
+    ImageData onePx = context.createImageData(1, 1); // will fail on FF3.0 (not such method)
+    assertEquals(1, onePx.getHeight());
+    assertEquals(1, onePx.getWidth());
+    
+    // set the image to a single blue pixel
+    CanvasPixelArray onePxArray = onePx.getData();
+    onePxArray.set(0, 000);
+    onePxArray.set(1, 000);
+    onePxArray.set(2, 255);
+    onePxArray.set(3, 255);
+    
+    // put the pixel at location 10, 10 on the canvas
+    context.putImageData(onePx, 10, 10);
+    ImageData verifyPx = context.getImageData(10, 10, 1, 1);
+    assertEquals(1, verifyPx.getWidth());
+    assertEquals(1, verifyPx.getHeight());
+    CanvasPixelArray verifyPxArray = verifyPx.getData();
+    assertEquals(onePxArray.get(0), verifyPxArray.get(0));
+    assertEquals(onePxArray.get(1), verifyPxArray.get(1));
+    assertEquals(onePxArray.get(2), verifyPxArray.get(2));
+    assertEquals(onePxArray.get(3), verifyPxArray.get(3));
+
+    // test that edge cases don't blow up: values outside the range 0...255
+    ImageData clampPixels = context.createImageData(3, 3); // will fail on FF3.0 (not such method)
+    CanvasPixelArray clampArraySet = clampPixels.getData();
+    try {
+      clampArraySet.set(2, -2);
+      clampArraySet.set(3, 270);
+      context.putImageData(clampPixels, 4f, 4f);
+    } catch (Exception e) {
+      fail("Should not throw exception when setting values outside the range of 0...255");
+    }
+    clampPixels = context.getImageData(4f, 4f, 3f, 3f);
+    CanvasPixelArray clampArrayGet = clampPixels.getData();
+    
+    // test that edge cases don't blow up: fall off the CanvasPixelArray end
+    int aPixel = clampArrayGet.get(9999);
+    assertEquals("CanvasPixelArray should return 0 for values off its end", aPixel, 0);
+    int bPixel = clampArrayGet.get(-9999);
+    assertEquals("CanvasPixelArray should return 0 for values off its end", bPixel, 0);
+  }
+
+  public void testIsPointInPath() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(40);
+    canvas1.setCoordinateSpaceWidth(60);
+
+    Context2d context = canvas1.getContext2d();
+    context.beginPath();
+    context.moveTo(10f, 10f);
+    context.lineTo(20f, 20f);
+    context.lineTo(20f, 10f);
+    context.closePath();
+    
+    assertTrue("Point should be in path", context.isPointInPath(18f, 12f));
+    assertFalse("Point should not be in path", context.isPointInPath(1f, 1f));
+  }
+
+  public void testLines() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(40);
+    canvas1.setCoordinateSpaceWidth(60);
+    
+    Context2d context = canvas1.getContext2d();
+    context.setFillStyle("#ff00ff");
+    context.setLineCap(LineCap.BUTT);
+    context.setLineJoin(LineJoin.BEVEL);
+    context.setLineWidth(2f);
+
+    context.beginPath();
+    context.moveTo(10f, 10f);
+    context.lineTo(20f, 20f);
+    context.lineTo(20f, 10f);
+    context.closePath();
+
+    assertEquals(LineCap.BUTT.getValue(), context.getLineCap());
+    assertEquals(LineJoin.BEVEL.getValue(), context.getLineJoin());
+    assertEquals(2f, context.getLineWidth());
+  }
+
+  public void testMiter() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    Context2d context = canvas1.getContext2d();
+    context.setMiterLimit(3f);
+    assertEquals(3f, context.getMiterLimit());
+  }
+
+  public void testPixelManipulation() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(40);
+    canvas1.setCoordinateSpaceWidth(60);
+    Context2d context = canvas1.getContext2d();
+
+    // fill the canvas with ff0000
+    context.setFillStyle("#ff0000");
+    context.fillRect(0, 0, 60, 40);
+    // fill the 1st and 2nd quadrants with green
+    context.setFillStyle("#00ff00");
+    context.fillRect(0, 0, 60, 20);
+    
+    ImageData imageData = context.getImageData(0, 0, 60, 40);
+    assertEquals("Pixels in first quadrant should be green", 0, imageData.getRedAt(45, 10));
+    assertEquals("Pixels in first quadrant should be green", 255, imageData.getGreenAt(45, 10));
+    assertEquals("Pixels in third quadrant should be red", 255, imageData.getRedAt(15, 30));
+    assertEquals("Pixels in third quadrant should be red", 0, imageData.getGreenAt(15, 30));
+  }
+
+  public void testShadows() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+    
+    // Firefox 3.0 returns the incorrect shadowBlur value so the following tests are disabled
+    // for FF 3.0 and before.
+    if (isGecko190OrBefore()) {
+      return;
+    }
+
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    canvas1.setCoordinateSpaceHeight(40);
+    canvas1.setCoordinateSpaceWidth(60);
+
+    Context2d context = canvas1.getContext2d();
+    context.setShadowBlur(3f);
+    context.setShadowColor("#ff00ff");
+    context.setShadowOffsetX(3f);
+    context.setShadowOffsetY(4f);
+    context.lineTo(60, 40);
+    assertEquals(3f, context.getShadowBlur());
+    assertEquals("#ff00ff", context.getShadowColor().toLowerCase());
+    assertEquals(3f, context.getShadowOffsetX());
+    assertEquals(4f, context.getShadowOffsetY());
+  }
+
+  public void testStrokeStyle() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+
+    // get the 2d contexts
+    Context2d context1 = canvas1.getContext2d();
+    Context2d context2 = canvas2.getContext2d();
+
+    // test that a color can be set and is correct
+    context1.setStrokeStyle(CssColor.make("#ffff00"));
+    FillStrokeStyle strokeStyleCol = context1.getStrokeStyle();
+    assertTrue("strokeStyleCol is a color", strokeStyleCol.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertFalse("strokeStyleCol is a color", strokeStyleCol.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertFalse("strokeStyleCol is a color", strokeStyleCol.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+    // test that a gradient can be set and is correct
+    CanvasGradient gradient = context1.createLinearGradient(0, 0, 10, 20);
+    gradient.addColorStop(0.5f, "#ff00ff");
+    context1.setStrokeStyle(gradient);
+    FillStrokeStyle strokeStyleGrad = context1.getStrokeStyle();
+    assertFalse("strokeStyleGrad is a gradient", strokeStyleGrad.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertTrue("strokeStyleGrad is a gradient", strokeStyleGrad.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertFalse("strokeStyleGrad is a gradient", strokeStyleGrad.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+    // test that a pattern can be set and is correct
+    CanvasPattern pattern = context1.createPattern(canvas1.getCanvasElement(), 
+        Context2d.Repetition.REPEAT);
+    context1.setStrokeStyle(pattern);
+    FillStrokeStyle strokeStylePat = context1.getStrokeStyle();
+    assertFalse("strokeStylePat is a pattern", strokeStylePat.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertFalse("strokeStylePat is a pattern", strokeStylePat.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertTrue("strokeStylePat is a pattern", strokeStylePat.getType() == FillStrokeStyle.TYPE_PATTERN);
+    
+    // test that a StrokeStyle can be passed around w/o knowing what it is
+    FillStrokeStyle strokeStyle = context1.getStrokeStyle();
+    context2.setStrokeStyle(strokeStyle);
+    FillStrokeStyle strokeStyle2 = context2.getStrokeStyle();
+    assertFalse("strokeStyle2 is a pattern", strokeStyle2.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+    assertFalse("strokeStyle2 is a pattern", strokeStyle2.getType() == FillStrokeStyle.TYPE_GRADIENT);
+    assertTrue("strokeStyle2 is a pattern", strokeStyle2.getType() == FillStrokeStyle.TYPE_PATTERN);
+  }
+
+  public void testText() {
+    if (!canvas1.isSupported()) {
+      return; // disable tests if not supported
+    }
+    
+    canvas1.setHeight("40px");
+    canvas1.setWidth("60px");
+    Context2d context = canvas1.getContext2d();
+    context.setFont("bold 40px sans-serif");
+    context.setTextAlign(TextAlign.CENTER);
+    context.setTextBaseline(TextBaseline.HANGING);
+    context.fillText("GWT", 50, 60);
+    assertEquals(TextAlign.CENTER.getValue(), context.getTextAlign());
+    assertEquals(TextBaseline.HANGING.getValue(), context.getTextBaseline());
+  }
+}