Bug 776685; throw TypeError exceptions from Azure canvas bindings. r=bz
authorNicholas Cameron <ncameron@mozilla.com>
Thu, 26 Jul 2012 14:31:26 +1200
changeset 101132 675d6bad2418e7a8954f3e041308cb92a6e3bf02
parent 101131 d14731ba12c16aa8ef9f46334070eb36f06c5039
child 101133 abf5ac19472ba2d1e77b6bbc3ec0c02e1d0a957e
push id23217
push userryanvm@gmail.com
push dateThu, 02 Aug 2012 02:18:43 +0000
treeherdermozilla-central@61d7f15ea6a7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs776685
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 776685; throw TypeError exceptions from Azure canvas bindings. r=bz
content/canvas/test/test_canvas.html
dom/bindings/Codegen.py
dom/bindings/Errors.msg
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -3533,18 +3533,23 @@ isPixel(ctx, 55,25, 0,255,0,255, 2);
 
 function test_2d_drawImage_null() {
 
 var canvas = document.getElementById('c121');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.drawImage(null, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-
+} catch (e) { _thrown = e };
+
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 }
 </script>
 
 <!-- [[[ test_2d.drawImage.outsidesource.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.outsidesource</p>
 <canvas id="c122" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -3710,27 +3715,49 @@ isPixel(ctx, 50,25, 0,255,0,255, 2);
 
 function test_2d_drawImage_wrongtype() {
 
 var canvas = document.getElementById('c127');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.drawImage(undefined, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e }; 
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
+
 var _thrown = undefined; try {
   ctx.drawImage(0, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
+
 var _thrown = undefined; try {
   ctx.drawImage("", 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
+
 var _thrown = undefined; try {
   ctx.drawImage(document.createElement('p'), 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 }
 </script>
 
 <!-- [[[ test_2d.drawImage.zerosource.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.zerosource</p>
 <!-- Testing: drawImage with zero-sized source rectangle throws INDEX_SIZE_ERR -->
@@ -7691,17 +7718,22 @@ var _thrown = undefined; try {
 
 function test_2d_imageData_create1_zero() {
 
 var canvas = document.getElementById('c262a');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createImageData(null);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown instanceof TypeError, "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 
 }
 </script>
 
 <!-- [[[ test_2d.imageData.get.basic.html ]]] -->
 
 <p>Canvas test: 2d.imageData.get.basic</p>
@@ -9202,18 +9234,22 @@ var _thrown = undefined; try {
 
 function test_2d_imageData_put_null() {
 
 var canvas = document.getElementById('c300');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.putImageData(null, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 }
 </script>
 
 <!-- [[[ test_2d.imageData.put.path.html ]]] -->
 
 <p>Canvas test: 2d.imageData.put.path</p>
 <!-- Testing: putImageData() does not affect the current path -->
@@ -9314,23 +9350,41 @@ for (var i = 0; i < imgdata2.data.length
 function test_2d_imageData_put_wrongtype() {
 
 var canvas = document.getElementById('c304');
 var ctx = canvas.getContext('2d');
 
 var imgdata = { width: 1, height: 1, data: [255, 0, 0, 255] };
 var _thrown = undefined; try {
   ctx.putImageData(imgdata, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e }; 
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
+
 var _thrown = undefined; try {
   ctx.putImageData("cheese", 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
+
 var _thrown = undefined; try {
   ctx.putImageData(42, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
+
 
 
 }
 </script>
 
 <!-- [[[ test_2d.line.cap.butt.html ]]] -->
 
 <p>Canvas test: 2d.line.cap.butt</p>
@@ -15126,17 +15180,22 @@ var _thrown = undefined; try {
 
 function test_2d_pattern_image_null() {
 
 var canvas = document.getElementById('c467');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern(null, 'repeat');
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 
 }
 </script>
 
 <!-- [[[ test_2d.pattern.image.string.html ]]] -->
 
 <p>Canvas test: 2d.pattern.image.string</p>
@@ -15145,17 +15204,22 @@ var _thrown = undefined; try {
 
 function test_2d_pattern_image_string() {
 
 var canvas = document.getElementById('c468');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern('image_red.png', 'repeat');
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 
 }
 </script>
 
 <!-- [[[ test_2d.pattern.image.undefined.html ]]] -->
 
 <p>Canvas test: 2d.pattern.image.undefined</p>
@@ -15164,17 +15228,22 @@ var _thrown = undefined; try {
 
 function test_2d_pattern_image_undefined() {
 
 var canvas = document.getElementById('c469');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern(undefined, 'repeat');
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} catch (e) { _thrown = e };
+if (IsAzureEnabled()) {
+  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+} else {
+  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
+}
 
 
 }
 </script>
 
 <!-- [[[ test_2d.pattern.modify.canvas1.html ]]] -->
 
 <p>Canvas test: 2d.pattern.modify.canvas1</p>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1508,23 +1508,26 @@ def getJSToNativeConversionTemplate(type
     """
     # If we have a defaultValue then we're not actually optional for
     # purposes of what we need to be declared as.
     assert(defaultValue is None or not isOptional)
 
     # Also, we should not have a defaultValue if we know we're an object
     assert(not isDefinitelyObject or defaultValue is None)
 
-    # A helper function for dealing with failures due to the JS value being the
+    # Helper functions for dealing with failures due to the JS value being the
     # wrong type of value
-    def onFailure(failureCode, isWorker):
+    def onFailureNotAnObject(failureCode):
         return CGWrapper(CGGeneric(
                 failureCode or
-                "return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);"
-                % toStringBool(isWorker)), post="\n")
+                'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n")
+    def onFailureBadType(failureCode, typeName):
+        return CGWrapper(CGGeneric(
+                failureCode or
+                'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n")
 
     # A helper function for handling default values.  Takes a template
     # body and the C++ code to set the default value and wraps the
     # given template body in handling for the default value.
     def handleDefault(template, setDefault):
         if defaultValue is None:
             return template
         return CGWrapper(
@@ -1542,30 +1545,30 @@ def getJSToNativeConversionTemplate(type
         if (defaultValue is not None and
             not isinstance(defaultValue, IDLNullValue)):
             raise TypeError("Can't handle non-null default value here")
         return handleDefault(template, codeToSetNull)
 
     # A helper function for wrapping up the template body for
     # possibly-nullable objecty stuff
     def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
-                           codeToSetNull, isWorker, failureCode=None):
+                           codeToSetNull, failureCode=None):
         if not isDefinitelyObject:
             # Handle the non-object cases by wrapping up the whole
             # thing in an if cascade.
             templateBody = (
                 "if (${val}.isObject()) {\n" +
                 CGIndenter(CGGeneric(templateBody)).define() + "\n")
             if type.nullable():
                 templateBody += (
                     "} else if (${val}.isNullOrUndefined()) {\n"
                     "  %s;\n" % codeToSetNull)
             templateBody += (
                 "} else {\n" +
-                CGIndenter(onFailure(failureCode, isWorker)).define() +
+                CGIndenter(onFailureNotAnObject(failureCode)).define() +
                 "}")
             if type.nullable():
                 templateBody = handleDefaultNull(templateBody, codeToSetNull)
             else:
                 assert(defaultValue is None)
 
         return templateBody
 
@@ -1641,18 +1644,17 @@ for (uint32_t i = 0; i < length; ++i) {
                         "valPtr": "&temp",
                         "declName" : "(*arr.AppendElement())"
                         }
                     ))).define()
 
         templateBody += "\n}"
         templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
                                           type,
-                                          "const_cast< %s & >(${declName}).SetNull()" % mutableTypeName.define(),
-                                          descriptorProvider.workers)
+                                          "const_cast< %s & >(${declName}).SetNull()" % mutableTypeName.define())
         return (templateBody, typeName, None, isOptional)
 
     if type.isUnion():
         if isMember:
             raise TypeError("Can't handle unions as members, we have a "
                             "holderType")
         nullable = type.nullable();
         if nullable:
@@ -1661,64 +1663,69 @@ for (uint32_t i = 0; i < length; ++i) {
         assert(defaultValue is None or
                (isinstance(defaultValue, IDLNullValue) and nullable))
 
         unionArgumentObj = "${holderName}"
         if isOptional or nullable:
             unionArgumentObj += ".ref()"
 
         memberTypes = type.flatMemberTypes
+        names = []
 
         interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
         if len(interfaceMemberTypes) > 0:
             interfaceObject = []
             for memberType in interfaceMemberTypes:
                 if type.isGeckoInterface():
                     name = memberType.inner.identifier.name
                 else:
                     name = memberType.name
                 interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
+                names.append(name)
             interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
         else:
             interfaceObject = None
 
         arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
         if len(arrayObjectMemberTypes) > 0:
             assert len(arrayObjectMemberTypes) == 1
             memberType = arrayObjectMemberTypes[0]
             name = memberType.name
             arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
             # XXX Now we're supposed to check for an array or a platform object
             # that supports indexed properties... skip that last for now. It's a
             # bit of a pain.
             arrayObject = CGWrapper(CGIndenter(arrayObject),
                                     pre="if (IsArrayLike(cx, &argObj)) {\n",
                                     post="}")
+            names.append(name)
         else:
             arrayObject = None
 
         dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
         if len(dateObjectMemberTypes) > 0:
             assert len(dateObjectMemberTypes) == 1
             memberType = dateObjectMemberTypes[0]
             name = memberType.name
             dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
                                    "done = true;" % (unionArgumentObj, name))
             dateObject = CGWrapper(CGIndenter(dateObject),
                                    pre="if (JS_ObjectIsDate(cx, &argObj)) {\n",
                                    post="\n}")
+            names.append(name)
         else:
             dateObject = None
 
         callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
         if len(callbackMemberTypes) > 0:
             assert len(callbackMemberTypes) == 1
             memberType = callbackMemberTypes[0]
             name = memberType.name
             callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+            names.append(name)
         else:
             callbackObject = None
 
         dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
         if len(dictionaryMemberTypes) > 0:
             raise TypeError("No support for unwrapping dictionaries as member "
                             "of a union")
         else:
@@ -1778,34 +1785,35 @@ for (uint32_t i = 0; i < length; ++i) {
         if len(otherMemberTypes) > 0:
             assert len(otherMemberTypes) == 1
             memberType = otherMemberTypes[0]
             if memberType.isEnum():
                 name = memberType.inner.identifier.name
             else:
                 name = memberType.name
             other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+            names.append(name)
             if hasObjectTypes:
                 other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
                 if object:
                     join = " else "
                 else:
                     other = CGWrapper(other, pre="if (!done) ")
                     join = "\n"
                 templateBody = CGList([templateBody, other], join)
         else:
             other = None
 
         templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
         throw = CGGeneric("if (failed) {\n"
                           "  return false;\n"
                           "}\n"
                           "if (!done) {\n"
-                          "  return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
-                          "}" % toStringBool(descriptorProvider.workers))
+                          "  return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
+                          "}" % ", ".join(names))
         templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
 
         typeName = type.name
         argumentTypeName = typeName + "Argument"
         if nullable:
             typeName = "Nullable<" + typeName + " >"
         if isOptional:
             nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
@@ -1934,18 +1942,18 @@ for (uint32_t i = 0; i < length; ++i) {
                 # will just own stuff.
                 templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
             else:
                 holderType = "nsRefPtr<" + typeName + ">"
             templateBody += (
                 "jsval tmpVal = ${val};\n" +
                 typePtr + " tmp;\n"
                 "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
-            templateBody += CGIndenter(onFailure(failureCode,
-                                                 descriptor.workers)).define()
+            templateBody += CGIndenter(onFailureBadType(failureCode,
+                                                        descriptor.interface.identifier.name)).define()
             templateBody += ("}\n"
                 "MOZ_ASSERT(tmp);\n")
 
             if not isDefinitelyObject:
                 # Our tmpVal will go out of scope, so we can't rely on it
                 # for rooting
                 templateBody += (
                     "if (tmpVal != ${val} && !${holderName}) {\n"
@@ -1954,17 +1962,17 @@ for (uint32_t i = 0; i < length; ++i) {
                     "  ${holderName} = tmp;\n"
                     "}\n")
 
             # And store our tmp, before it goes out of scope.
             templateBody += "${declName} = tmp;"
 
         templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
                                           type, "${declName} = NULL",
-                                          descriptor.workers, failureCode)
+                                          failureCode)
 
         declType = CGGeneric(declType)
         if holderType is not None:
             holderType = CGGeneric(holderType)
         return (templateBody, declType, holderType, isOptional)
 
     if type.isSpiderMonkeyInterface():
         if isMember:
@@ -1992,34 +2000,33 @@ for (uint32_t i = 0; i < length; ++i) {
                 constructLoc = "(const_cast<Optional<" + name + ">& >(${declName}))"
                 constructMethod = "Construct"
                 constructInternal = "Value"
             else:
                 declType = "NonNull<" + name + ">"
         template = (
             "%s.%s(cx, &${val}.toObject());\n"
             "if (!%s.%s().inited()) {\n"
-            "%s" # No newline here because onFailure() handles that
+            "%s" # No newline here because onFailureBadType() handles that
             "}\n" %
             (constructLoc, constructMethod, constructLoc, constructInternal,
-             CGIndenter(onFailure(failureCode, descriptorProvider.workers)).define()))
+             CGIndenter(onFailureBadType(failureCode, type.name)).define()))
         nullableTarget = ""
         if type.nullable():
             if isOptional:
                 mutableDecl = "(const_cast<Optional<" + name + "*>& >(${declName}))"
                 template += "%s.Construct();\n" % mutableDecl
                 nullableTarget = "%s.Value()" % mutableDecl
             else:
                 nullableTarget = "${declName}"
             template += "%s = ${holderName}.addr();" % nullableTarget
         elif not isOptional:
             template += "${declName} = ${holderName}.addr();"
         template = wrapObjectTemplate(template, isDefinitelyObject, type,
                                       "%s = NULL" % nullableTarget,
-                                      descriptorProvider.workers,
                                       failureCode)
 
         if holderType is not None:
             holderType = CGGeneric(holderType)
         # We handle all the optional stuff ourselves; no need for caller to do it.
         return (template, CGGeneric(declType), holderType, False)
 
     if type.isString():
@@ -2143,17 +2150,17 @@ for (uint32_t i = 0; i < length; ++i) {
 
     if type.isObject():
         if isMember:
             raise TypeError("Can't handle member 'object'; need to sort out "
                             "rooting issues")
         template = wrapObjectTemplate("${declName} = &${val}.toObject();",
                                       isDefinitelyObject, type,
                                       "${declName} = NULL",
-                                      descriptorProvider.workers, failureCode)
+                                      failureCode)
         if type.nullable():
             declType = CGGeneric("JSObject*")
         else:
             declType = CGGeneric("NonNull<JSObject>")
         return (template, declType, None, isOptional)
 
     if type.isDictionary():
         if failureCode is not None:
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -14,10 +14,13 @@
  * <ARGUMENT_COUNT> is an integer literal specifying the total number of
  * replaceable arguments in the following format string.
  *
  * <FORMAT_STRING> is a string literal, containing <ARGUMENT_COUNT> sequences
  * {X} where X  is an integer representing the argument number that will
  * be replaced with a string value when the error is reported.
  */
 
-MSG_DEF(MSG_INVALID_ENUM_VALUE, 2, "Value '{0}' is not a valid value for enumeration '{1}'.")
+MSG_DEF(MSG_INVALID_ENUM_VALUE, 2, "Value '{0}' is not a valid value for enumeration {1}.")
 MSG_DEF(MSG_MISSING_ARGUMENTS, 1, "Not enough arguments to {0}.")
+MSG_DEF(MSG_NOT_OBJECT, 0, "Value not an object.")
+MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 1, "Value does not implement interface {0}.")
+MSG_DEF(MSG_NOT_IN_UNION, 1, "Value could not be converted to any of: {0}.")