Improve canvas for browsers (and permutations) with partial canvas support.
Review at http://gwt-code-reviews.appspot.com/1296801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9602 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
index 3b1da45..ea94f47 100644
--- a/user/src/com/google/gwt/canvas/Canvas.gwt.xml
+++ b/user/src/com/google/gwt/canvas/Canvas.gwt.xml
@@ -16,5 +16,30 @@
<module>
<inherits name="com.google.gwt.user.User"/>
<inherits name="com.google.gwt.canvas.dom.DOM"/>
+
+ <!-- Define the support property -->
+ <define-property name="canvasElementSupport" values="maybe,no" />
+
+ <!-- Give default support value of no -->
+ <set-property name="canvasElementSupport" value="no" />
+
+ <set-property name="canvasElementSupport" value="maybe">
+ <any>
+ <when-property-is name="user.agent" value="safari" />
+ <when-property-is name="user.agent" value="gecko1_8" />
+ <when-property-is name="user.agent" value="opera" />
+ </any>
+ </set-property>
+
+ <replace-with class="com.google.gwt.canvas.client.Canvas.CanvasElementSupportDetectedMaybe">
+ <when-type-is class="com.google.gwt.canvas.client.Canvas.CanvasElementSupportDetector" />
+ <when-property-is name="canvasElementSupport" value="maybe" />
+ </replace-with>
+
+ <replace-with class="com.google.gwt.canvas.client.Canvas.CanvasElementSupportDetectedNo">
+ <when-type-is class="com.google.gwt.canvas.client.Canvas.CanvasElementSupportDetector" />
+ <when-property-is name="canvasElementSupport" value="no" />
+ </replace-with>
+
<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
index c6372b3..13a530b 100644
--- a/user/src/com/google/gwt/canvas/client/Canvas.java
+++ b/user/src/com/google/gwt/canvas/client/Canvas.java
@@ -17,9 +17,10 @@
import com.google.gwt.canvas.dom.client.Context;
import com.google.gwt.canvas.dom.client.Context2d;
+import com.google.gwt.core.client.GWT;
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.dom.client.PartialSupport;
import com.google.gwt.user.client.ui.FocusWidget;
/**
@@ -31,25 +32,55 @@
* </span>
* </p>
*
- * This widget may not be supported on all browsers, see {@link IsSupported}
+ * This widget may not be supported on all browsers.
*/
-public class Canvas extends FocusWidget implements IsSupported {
+@PartialSupport
+public class Canvas extends FocusWidget {
+ private static CanvasElementSupportDetector detector;
+
+ /**
+ * Return a new {@link Canvas} if supported, and null otherwise.
+ *
+ * @return a new {@link Canvas} if supported, and null otherwise
+ */
+ public static Canvas createIfSupported() {
+ if (detector == null) {
+ detector = GWT.create(CanvasElementSupportDetector.class);
+ }
+ if (!detector.isSupportedCompileTime()) {
+ return null;
+ }
+ CanvasElement element = Document.get().createCanvasElement();
+ if (!detector.isSupportedRunTime(element)) {
+ return null;
+ }
+ return new Canvas(element);
+ }
/**
* 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;
- }-*/;
+ public static boolean isSupported() {
+ if (detector == null) {
+ detector = GWT.create(CanvasElementSupportDetector.class);
+ }
+ if (!detector.isSupportedCompileTime()) {
+ return false;
+ }
+ CanvasElement element = Document.get().createCanvasElement();
+ if (!detector.isSupportedRunTime(element)) {
+ return false;
+ }
+ return true;
+ }
/**
- * Creates a Canvas.
+ * Protected constructor. Use {@link #createIfSupported()} to create a Canvas.
*/
- public Canvas() {
- setElement(Document.get().createCanvasElement());
+ private Canvas(CanvasElement element) {
+ setElement(element);
}
/**
@@ -142,4 +173,67 @@
public String toDataUrl(String type) {
return getCanvasElement().toDataUrl(type);
}
+
+ /**
+ * Detector for browser support of {@link CanvasElement}.
+ */
+ private static class CanvasElementSupportDetector {
+ /**
+ * Using a run-time check, return true if the {@link CanvasElement} is
+ * supported.
+ *
+ * @return true if supported, false otherwise.
+ */
+ static native boolean isSupportedRunTime(CanvasElement element) /*-{
+ return !!element.getContext;
+ }-*/;
+
+ /**
+ * Using a compile-time check, return true if {@link CanvasElement} might
+ * be supported.
+ *
+ * @return true if might be supported, false otherwise.
+ */
+ boolean isSupportedCompileTime() {
+ // will be true in CanvasElementSupportDetectedMaybe
+ // will be false in CanvasElementSupportDetectedNo
+ return false;
+ }
+ }
+
+ /**
+ * Detector for permutations that might support {@link CanvasElement}.
+ */
+ @SuppressWarnings("unused")
+ private static class CanvasElementSupportDetectedMaybe
+ extends CanvasElementSupportDetector {
+ /**
+ * Using a compile-time check, return true if {@link CanvasElement} might be
+ * supported.
+ *
+ * @return true if might be supported, false otherwise.
+ */
+ @Override
+ boolean isSupportedCompileTime() {
+ return true;
+ }
+ }
+
+ /**
+ * Detector for permutations that do not support {@link CanvasElement}.
+ */
+ @SuppressWarnings("unused")
+ private static class CanvasElementSupportDetectedNo
+ extends CanvasElementSupportDetector {
+ /**
+ * Using a compile-time check, return true if {@link CanvasElement} might be
+ * supported.
+ *
+ * @return true if might be supported, false otherwise.
+ */
+ @Override
+ boolean isSupportedCompileTime() {
+ return false;
+ }
+ }
}
diff --git a/user/src/com/google/gwt/dom/client/PartialSupport.java b/user/src/com/google/gwt/dom/client/PartialSupport.java
new file mode 100644
index 0000000..b8b4d20
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/PartialSupport.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * Annotation for classes that are only supported on a limited set of browsers.
+ *
+ * <p>
+ * By convention, classes annotated with PartialSupport will provide two static
+ * methods:
+ * <ol>
+ * <li> <code>static boolean isSupported()</code> that returns whether the
+ * feature is supported. </li>
+ * <li> <code>static YourType createIfSupported()</code> factory method that
+ * returns a new feature if supported, or null otherwise. </li>
+ * </ol>
+ * </p>
+ */
+public @interface PartialSupport {
+
+}
diff --git a/user/src/com/google/gwt/user/client/IsSupported.java b/user/src/com/google/gwt/user/client/IsSupported.java
deleted file mode 100644
index f439842..0000000
--- a/user/src/com/google/gwt/user/client/IsSupported.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- *
- * <p>
- * <span style="color:red">Experimental API: This API is still under development
- * and is subject to change.
- * </span>
- * </p>
- *
- * 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/client/CanvasTest.java b/user/test/com/google/gwt/canvas/client/CanvasTest.java
index 147ddfe..d1b308a 100644
--- a/user/test/com/google/gwt/canvas/client/CanvasTest.java
+++ b/user/test/com/google/gwt/canvas/client/CanvasTest.java
@@ -45,10 +45,11 @@
@Override
protected void gwtSetUp() throws Exception {
- canvas1 = new Canvas();
- canvas2 = new Canvas();
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ canvas1 = Canvas.createIfSupported();
+ canvas2 = Canvas.createIfSupported();
+
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
RootPanel.get().add(canvas1);
@@ -57,24 +58,29 @@
@Override
protected void gwtTearDown() throws Exception {
+ if (canvas1 == null) {
+ return; // don't continue if not supported
+ }
+
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.)
- *
+ * 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
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
- // Safari 3.0 does not support toDataURL(), so the following tests are disabled for
- // Safari 3.0 and before.
+ // Safari 3.0 does not support toDataURL(), so the following tests are
+ // disabled for Safari 3.0 and before.
if (isWebkit525OrBefore()) {
return;
}
@@ -87,12 +93,34 @@
canvas1.setCoordinateSpaceWidth(0);
String dataUrl = canvas1.toDataUrl();
- assertTrue("toDataURL() should return data:something", dataUrl.startsWith("data:"));
+ assertTrue("toDataURL() should return data:something",
+ dataUrl.startsWith("data:"));
+ }
+
+ public void testDataUrlWithType() {
+ if (canvas1 == null) {
+ return; // don't continue if not supported
+ }
+
+ // Safari 3.0 does not support toDataURL(), so the following tests are
+ // disabled for Safari 3.0 and before.
+ if (isWebkit525OrBefore()) {
+ return;
+ }
+
+ 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"));
}
public void testHeightAndWidth() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
canvas1.setHeight("40px");
@@ -117,8 +145,8 @@
}
public void testInternalHeightAndWidth() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
canvas1.setHeight("40px");
@@ -147,24 +175,14 @@
assertEquals(161, canvas1.getCoordinateSpaceWidth());
}
- public void testDataUrlWithType() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ public void testIsSupported() {
+ if (canvas1 == null) {
+ assertFalse(
+ "isSupported() should be false when createIfSupported() returns null",
+ Canvas.isSupported());
+ } else {
+ assertTrue(
+ "isSupported() should be true when createIfSupported() returns non-null", Canvas.isSupported());
}
-
- // Safari 3.0 does not support toDataURL(), so the following tests are disabled for
- // Safari 3.0 and before.
- if (isWebkit525OrBefore()) {
- return;
- }
-
- 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
index 666ff46..259cf9a 100644
--- a/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
+++ b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
@@ -54,10 +54,11 @@
@Override
protected void gwtSetUp() throws Exception {
- canvas1 = new Canvas();
- canvas2 = new Canvas();
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ canvas1 = Canvas.createIfSupported();
+ canvas2 = Canvas.createIfSupported();
+
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
RootPanel.get().add(canvas1);
@@ -71,8 +72,8 @@
}
public void testArc() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
canvas1.setHeight("40px");
@@ -95,8 +96,8 @@
}
public void testFillRect() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// Safari 3.0 does not support getImageData(), so the following tests are disabled for
@@ -150,8 +151,8 @@
}
public void testFillStyle() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// get the 2d contexts
@@ -193,8 +194,8 @@
}
public void testFont() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
Context2d context = canvas1.getContext2d();
@@ -203,8 +204,8 @@
}
public void testGlobalAlpha() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
Context2d context = canvas1.getContext2d();
@@ -214,8 +215,8 @@
// This test currently fails on IE9 and is disabled.
public void disabled_testGlobalComposite() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
Context2d context = canvas1.getContext2d();
@@ -226,8 +227,8 @@
}
public void testGradient() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// Safari 3.0 does not support getImageData(), so the following tests are disabled for
@@ -272,8 +273,8 @@
}
public void testImageData() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// Firefox 3.0 does not support createImageData(), so the following tests are disabled
@@ -342,8 +343,8 @@
}
public void testIsPointInPath() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
canvas1.setHeight("40px");
@@ -363,8 +364,8 @@
}
public void testLines() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
canvas1.setHeight("40px");
@@ -390,8 +391,8 @@
}
public void testMiter() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
Context2d context = canvas1.getContext2d();
@@ -400,8 +401,8 @@
}
public void testPixelManipulation() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// Safari 3.0 does not support getImageData(), so the following tests are disabled for
@@ -431,8 +432,8 @@
}
public void testShadows() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// Firefox 3.0 returns the incorrect shadowBlur value so the following tests are disabled
@@ -459,8 +460,8 @@
}
public void testStrokeStyle() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
// get the 2d contexts
@@ -502,8 +503,8 @@
}
public void testText() {
- if (!canvas1.isSupported()) {
- return; // disable tests if not supported
+ if (canvas1 == null) {
+ return; // don't continue if not supported
}
canvas1.setHeight("40px");