b=534467; implement Uint8ClampedArray for CanvasPixelArray; part 3, DOM-side implementation; r=bz
authorVladimir Vukicevic <vladimir@pobox.com>
Wed, 03 Mar 2010 16:40:27 -0800
changeset 38927 c1cc02524fbb38c57c5e1f2564d9acd9344bda77
parent 38926 92662f367b6ee597513429111ee4aafbd350480a
child 38928 cf1240a63d4e294fd45f3d6147191c16d82dcb74
push id11939
push uservladimir@mozilla.com
push dateThu, 04 Mar 2010 07:09:46 +0000
treeherdermozilla-central@cf1240a63d4e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs534467
milestone1.9.3a3pre
b=534467; implement Uint8ClampedArray for CanvasPixelArray; part 3, DOM-side implementation; r=bz
content/canvas/src/CustomQS_Canvas2D.h
content/canvas/src/nsCanvasRenderingContext2D.cpp
dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/tests/js1_8_5/extensions/typedarray.js
js/src/xpconnect/src/dom_quickstubs.qsconf
--- a/content/canvas/src/CustomQS_Canvas2D.h
+++ b/content/canvas/src/CustomQS_Canvas2D.h
@@ -1,46 +1,48 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (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.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
- * The Original Code is mozilla.org code.
+ * The Original Code is Gecko code.
  *
  * The Initial Developer of the Original Code is
- *   Mozilla Corporation.
+ *   Mozilla Corporation
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Vladimir Vukicevic <vladimir@pobox.com> (original author)
  *
  * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "nsDOMError.h"
 #include "nsIDOMCanvasRenderingContext2D.h"
 
 typedef nsresult (NS_STDCALL nsIDOMCanvasRenderingContext2D::*CanvasStyleSetterType)(const nsAString &, nsISupports *);
 typedef nsresult (NS_STDCALL nsIDOMCanvasRenderingContext2D::*CanvasStyleGetterType)(nsAString &, nsISupports **, PRInt32 *);
 
 static JSBool
 Canvas2D_SetStyleHelper(JSContext *cx, JSObject *obj, jsval id, jsval *vp,
                         CanvasStyleSetterType setfunc)
@@ -137,8 +139,221 @@ nsIDOMCanvasRenderingContext2D_SetFillSt
     return Canvas2D_SetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::SetFillStyle_multi);
 }
 
 static JSBool
 nsIDOMCanvasRenderingContext2D_GetFillStyle(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
     return Canvas2D_GetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::GetFillStyle_multi);
 }
+
+static JSBool
+nsIDOMCanvasRenderingContext2D_CreateImageData(JSContext *cx, uintN argc, jsval *vp)
+{
+    XPC_QS_ASSERT_CONTEXT_OK(cx);
+
+    /* Note: this doesn't need JS_THIS_OBJECT */
+
+    if (argc < 2)
+        return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+
+    jsval *argv = JS_ARGV(cx, vp);
+
+    int32 wi, hi;
+    if (!JS_ValueToECMAInt32(cx, argv[0], &wi) ||
+        !JS_ValueToECMAInt32(cx, argv[1], &hi))
+        return JS_FALSE;
+
+    if (wi <= 0 || hi <= 0)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    uint32 w = (uint32) wi;
+    uint32 h = (uint32) hi;
+
+    /* Sanity check w * h here */
+    uint32 len0 = w * h;
+    if (len0 / w != (uint32) h)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    uint32 len = len0 * 4;
+    if (len / 4 != len0)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    // create the fast typed array; it's initialized to 0 by default
+    JSObject *darray = js_CreateTypedArray(cx, js::TypedArray::TYPE_UINT8_CLAMPED, len);
+    JSAutoTempValueRooter rd(cx, darray);
+    if (!darray)
+        return JS_FALSE;
+
+    // Do JS_NewObject after CreateTypedArray, so that gc will get
+    // triggered here if necessary
+    JSObject *result = JS_NewObject(cx, NULL, NULL, NULL);
+    JSAutoTempValueRooter rr(cx, result);
+    if (!result)
+        return JS_FALSE;
+
+    if (!JS_DefineProperty(cx, result, "width", INT_TO_JSVAL(w), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) ||
+        !JS_DefineProperty(cx, result, "height", INT_TO_JSVAL(h), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) ||
+        !JS_DefineProperty(cx, result, "data", OBJECT_TO_JSVAL(darray), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+        return JS_FALSE;
+
+    *vp = OBJECT_TO_JSVAL(result);
+    return JS_TRUE;
+}
+
+static JSBool
+nsIDOMCanvasRenderingContext2D_GetImageData(JSContext *cx, uintN argc, jsval *vp)
+{
+    XPC_QS_ASSERT_CONTEXT_OK(cx);
+
+    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    if (!obj)
+        return JS_FALSE;
+
+    nsresult rv;
+
+    nsIDOMCanvasRenderingContext2D *self;
+    xpc_qsSelfRef selfref;
+    JSAutoTempValueRooter tvr(cx);
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull))
+        return JS_FALSE;
+
+    if (argc < 4)
+        return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+
+    jsval *argv = JS_ARGV(cx, vp);
+
+    int32 x, y;
+    int32 wi, hi;
+    if (!JS_ValueToECMAInt32(cx, argv[0], &x) ||
+        !JS_ValueToECMAInt32(cx, argv[1], &y) ||
+        !JS_ValueToECMAInt32(cx, argv[2], &wi) ||
+        !JS_ValueToECMAInt32(cx, argv[3], &hi))
+        return JS_FALSE;
+
+    if (wi <= 0 || hi <= 0)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    uint32 w = (uint32) wi;
+    uint32 h = (uint32) hi;
+
+    // Sanity check w * h here
+    uint32 len0 = w * h;
+    if (len0 / w != (uint32) h)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    uint32 len = len0 * 4;
+    if (len / 4 != len0)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    // create the fast typed array
+    JSObject *darray = js_CreateTypedArray(cx, js::TypedArray::TYPE_UINT8_CLAMPED, len);
+    JSAutoTempValueRooter rd(cx, darray);
+    if (!darray)
+        return JS_FALSE;
+
+    js::TypedArray *tdest = js::TypedArray::fromJSObject(darray);
+
+    // make the call
+    rv = self->GetImageData_explicit(x, y, w, h, (PRUint8*) tdest->data, tdest->byteLength);
+    if (NS_FAILED(rv))
+        return xpc_qsThrowMethodFailed(cx, rv, vp);
+
+    // Do JS_NewObject after CreateTypedArray, so that gc will get
+    // triggered here if necessary
+    JSObject *result = JS_NewObject(cx, NULL, NULL, NULL);
+    JSAutoTempValueRooter rr(cx, result);
+    if (!result)
+        return JS_FALSE;
+
+    if (!JS_DefineProperty(cx, result, "width", INT_TO_JSVAL(w), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) ||
+        !JS_DefineProperty(cx, result, "height", INT_TO_JSVAL(h), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) ||
+        !JS_DefineProperty(cx, result, "data", OBJECT_TO_JSVAL(darray), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+        return JS_FALSE;
+
+    *vp = OBJECT_TO_JSVAL(result);
+    return JS_TRUE;
+}
+
+static JSBool
+nsIDOMCanvasRenderingContext2D_PutImageData(JSContext *cx, uintN argc, jsval *vp)
+{
+    XPC_QS_ASSERT_CONTEXT_OK(cx);
+
+    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    if (!obj)
+        return JS_FALSE;
+
+    nsresult rv;
+
+    nsIDOMCanvasRenderingContext2D *self;
+    xpc_qsSelfRef selfref;
+    JSAutoTempValueRooter tvr(cx);
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull))
+        return JS_FALSE;
+
+    if (argc < 3)
+        return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+
+    jsval *argv = JS_ARGV(cx, vp);
+
+    if (JSVAL_IS_PRIMITIVE(argv[0]))
+        return xpc_qsThrow(cx, NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+
+    JSObject *dataObject = JSVAL_TO_OBJECT(argv[0]);
+    int32 x, y;
+    if (!JS_ValueToECMAInt32(cx, argv[1], &x) ||
+        !JS_ValueToECMAInt32(cx, argv[2], &y))
+        return JS_FALSE;
+
+    int32 wi, hi;
+    JSObject *darray;
+
+    // grab width, height, and the dense array from the dataObject
+    JSAutoTempValueRooter tv(cx);
+
+    if (!JS_GetProperty(cx, dataObject, "width", tv.addr()) ||
+        !JS_ValueToECMAInt32(cx, tv.value(), &wi))
+        return JS_FALSE;
+
+    if (!JS_GetProperty(cx, dataObject, "height", tv.addr()) ||
+        !JS_ValueToECMAInt32(cx, tv.value(), &hi))
+        return JS_FALSE;
+
+    if (wi <= 0 || hi <= 0)
+        return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+    uint32 w = (uint32) wi;
+    uint32 h = (uint32) hi;
+
+    if (!JS_GetProperty(cx, dataObject, "data", tv.addr()) ||
+        JSVAL_IS_PRIMITIVE(tv.value()))
+        return JS_FALSE;
+    darray = JSVAL_TO_OBJECT(tv.value());
+
+    JSAutoTempValueRooter tsrc_tvr(cx);
+
+    js::TypedArray *tsrc = NULL;
+    if (darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_UINT8] ||
+        darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_UINT8_CLAMPED])
+    {
+        tsrc = js::TypedArray::fromJSObject(darray);
+    } else if (JS_IsArrayObject(cx, darray) || js_IsTypedArray(darray)) {
+        // ugh, this isn't a uint8 typed array, someone made their own object; convert it to a typed array
+        JSObject *nobj = js_CreateTypedArrayWithArray(cx, js::TypedArray::TYPE_UINT8, darray);
+        if (!nobj)
+            return JS_FALSE;
+
+        *tsrc_tvr.addr() = OBJECT_TO_JSVAL(nobj);
+        tsrc = js::TypedArray::fromJSObject(nobj);
+    } else {
+        // yeah, no.
+        return xpc_qsThrow(cx, NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+    }
+
+    // make the call
+    rv = self->PutImageData_explicit(x, y, w, h, (PRUint8*) tsrc->data, tsrc->byteLength);
+    if (NS_FAILED(rv))
+        return xpc_qsThrowMethodFailed(cx, rv, vp);
+
+    *vp = JSVAL_VOID;
+    return JS_TRUE;
+}
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -3421,172 +3421,92 @@ nsCanvasRenderingContext2D::EnsureUnprem
   for (int a = 1; a <= 255; a++) {
     for (int c = 0; c <= 255; c++) {
       sUnpremultiplyTable[a][c] = (PRUint8)((c * 255) / a);
     }
   }
 }
 
 
-// ImageData getImageData (in float x, in float y, in float width, in float height);
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetImageData()
 {
+    /* Should never be called -- GetImageData_explicit is the QS entry point */
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
+                                                  PRUint8 *aData, PRUint32 aDataLen)
+{
     if (!mValid)
         return NS_ERROR_FAILURE;
 
     if (mCanvasElement && mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerTrustedForRead()) {
         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
         return NS_ERROR_DOM_SECURITY_ERR;
     }
 
-    nsAXPCNativeCallContext *ncc = nsnull;
-    nsresult rv = nsContentUtils::XPConnect()->
-        GetCurrentNativeCallContext(&ncc);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!ncc)
-        return NS_ERROR_FAILURE;
-
-    JSContext *ctx = nsnull;
-
-    rv = ncc->GetJSContext(&ctx);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRUint32 argc;
-    jsval *argv = nsnull;
-
-    ncc->GetArgc(&argc);
-    ncc->GetArgvPtr(&argv);
-
-    JSAutoRequest ar(ctx);
-
-    int32 x, y, w, h;
-    if (!JS_ConvertArguments (ctx, argc, argv, "jjjj", &x, &y, &w, &h))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
     if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
         return NS_ERROR_DOM_SYNTAX_ERR;
 
-    nsAutoArrayPtr<PRUint8> surfaceData (new (std::nothrow) PRUint8[w * h * 4]);
-    int surfaceDataStride = w*4;
-    int surfaceDataOffset = 0;
-
-    if (!surfaceData)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsRefPtr<gfxImageSurface> tmpsurf = new gfxImageSurface(surfaceData,
+    PRUint32 len = w * h * 4;
+    if (aDataLen != len)
+        return NS_ERROR_DOM_SYNTAX_ERR;
+
+    /* Copy the surface contents to the buffer */
+    nsRefPtr<gfxImageSurface> tmpsurf = new gfxImageSurface(aData,
                                                             gfxIntSize(w, h),
                                                             w * 4,
                                                             gfxASurface::ImageFormatARGB32);
     if (!tmpsurf || tmpsurf->CairoStatus())
         return NS_ERROR_FAILURE;
 
     nsRefPtr<gfxContext> tmpctx = new gfxContext(tmpsurf);
 
     if (!tmpctx || tmpctx->HasError())
         return NS_ERROR_FAILURE;
 
     tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
     tmpctx->SetSource(mSurface, gfxPoint(-(int)x, -(int)y));
     tmpctx->Paint();
 
-    tmpctx = nsnull;
-    tmpsurf = nsnull;
-
-    PRUint32 len = w * h * 4;
-    if (len > (((PRUint32)0xfff00000)/sizeof(jsval)))
-        return NS_ERROR_INVALID_ARG;
-
-    jsval *dest;
-    JSObject *dataArray = js_NewArrayObjectWithCapacity(ctx, len, &dest);
-    if (!dataArray)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
+    // make sure sUnpremultiplyTable has been created
     EnsureUnpremultiplyTable();
 
-    PRUint8 *row;
+    // NOTE! dst is the same as src, and this relies on reading
+    // from src and advancing that ptr before writing to dst.
+    PRUint8 *src = aData;
+    PRUint8 *dst = aData;
+
     for (int j = 0; j < h; j++) {
-        row = surfaceData + surfaceDataOffset + (surfaceDataStride * j);
         for (int i = 0; i < w; i++) {
+            // XXX Is there some useful swizzle MMX we can use here?
 #ifdef IS_LITTLE_ENDIAN
-            PRUint8 b = *row++;
-            PRUint8 g = *row++;
-            PRUint8 r = *row++;
-            PRUint8 a = *row++;
+            PRUint8 b = *src++;
+            PRUint8 g = *src++;
+            PRUint8 r = *src++;
+            PRUint8 a = *src++;
 #else
-            PRUint8 a = *row++;
-            PRUint8 r = *row++;
-            PRUint8 g = *row++;
-            PRUint8 b = *row++;
+            PRUint8 a = *src++;
+            PRUint8 r = *src++;
+            PRUint8 g = *src++;
+            PRUint8 b = *src++;
 #endif
             // Convert to non-premultiplied color
-
-            *dest++ = INT_TO_JSVAL(sUnpremultiplyTable[a][r]);
-            *dest++ = INT_TO_JSVAL(sUnpremultiplyTable[a][g]);
-            *dest++ = INT_TO_JSVAL(sUnpremultiplyTable[a][b]);
-            *dest++ = INT_TO_JSVAL(a);
+            *dst++ = sUnpremultiplyTable[a][r];
+            *dst++ = sUnpremultiplyTable[a][g];
+            *dst++ = sUnpremultiplyTable[a][b];
+            *dst++ = a;
         }
     }
 
-    // Allocate result object after array, so if we have to trigger gc
-    // we do it now.
-    JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL);
-    if (!result)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsAutoGCRoot resultGCRoot(&result, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) ||
-        !JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) ||
-        !JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0))
-        return NS_ERROR_FAILURE;
-
-    jsval *retvalPtr;
-    ncc->GetRetValPtr(&retvalPtr);
-    *retvalPtr = OBJECT_TO_JSVAL(result);
-    ncc->SetReturnValueWasSet(PR_TRUE);
-
     return NS_OK;
 }
 
-static inline PRUint8 ToUint8(jsint aInput)
-{
-    if (PRUint32(aInput) > 255)
-        return (aInput < 0) ? 0 : 255;
-    return PRUint8(aInput);
-}
-
-static inline PRUint8 ToUint8(double aInput)
-{
-    if (!(aInput >= 0)) /* Not < so that NaN coerces to 0 */
-        return 0;
-    if (aInput > 255)
-        return 255;
-    double toTruncate = aInput + 0.5;
-    PRUint8 retval = PRUint8(toTruncate);
-
-    // now retval is rounded to nearest, ties rounded up.  We want
-    // rounded to nearest ties to even, so check whether we had a tie.
-    if (retval == toTruncate) {
-        // It was a tie (since adding 0.5 gave us the exact integer we want).
-        // Since we rounded up, we either already have an even number or we
-        // have an odd number but the number we want is one less.  So just
-        // unconditionally masking out the ones bit should do the trick to get
-        // us the value we want.
-        return (retval & ~1);
-    }
-
-    return retval;
-}
-
 void
 nsCanvasRenderingContext2D::EnsurePremultiplyTable() {
   if (sPremultiplyTable)
     return;
 
   // Infallably alloc the premultiply table.
   sPremultiplyTable = new PRUint8[256][256];
 
@@ -3600,187 +3520,84 @@ nsCanvasRenderingContext2D::EnsurePremul
     }
   }
 }
 
 // void putImageData (in ImageData d, in float x, in float y);
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::PutImageData()
 {
+    /* Should never be called -- PutImageData_explicit is the QS entry point */
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
+                                                  unsigned char *aData, PRUint32 aDataLen)
+{
     nsresult rv;
 
     if (!mValid)
         return NS_ERROR_FAILURE;
 
-    nsAXPCNativeCallContext *ncc = nsnull;
-    rv = nsContentUtils::XPConnect()->
-        GetCurrentNativeCallContext(&ncc);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!ncc)
-        return NS_ERROR_FAILURE;
-
-    JSContext *ctx = nsnull;
-
-    rv = ncc->GetJSContext(&ctx);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRUint32 argc;
-    jsval *argv = nsnull;
-
-    ncc->GetArgc(&argc);
-    ncc->GetArgvPtr(&argv);
-
-    JSAutoRequest ar(ctx);
-
-    JSObject *dataObject;
-    int32 x, y;
-
-    if (!JS_ConvertArguments (ctx, argc, argv, "ojj", &dataObject, &x, &y))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    if (!dataObject)
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    int32 w, h;
-    JSObject *dataArray;
-    jsval v;
-
-    if (!JS_GetProperty(ctx, dataObject, "width", &v) ||
-        !JS_ValueToInt32(ctx, v, &w))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    if (!JS_GetProperty(ctx, dataObject, "height", &v) ||
-        !JS_ValueToInt32(ctx, v, &h))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    if (!JS_GetProperty(ctx, dataObject, "data", &v) ||
-        !JSVAL_IS_OBJECT(v))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-    dataArray = JSVAL_TO_OBJECT(v);
-
     if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
         return NS_ERROR_DOM_SYNTAX_ERR;
 
-    jsuint arrayLen;
-    if (!JS_IsArrayObject(ctx, dataArray) ||
-        !JS_GetArrayLength(ctx, dataArray, &arrayLen) ||
-        arrayLen < (jsuint)(w * h * 4))
+    PRUint32 len = w * h * 4;
+    if (aDataLen != len)
         return NS_ERROR_DOM_SYNTAX_ERR;
 
-    nsAutoArrayPtr<PRUint8> imageBuffer(new (std::nothrow) PRUint8[w * h * 4]);
-    if (!imageBuffer)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    PRUint8 *imgPtr = imageBuffer.get();
-
-    EnsurePremultiplyTable();
-
-    JSBool canFastPath =
-        js_CoerceArrayToCanvasImageData(dataArray, 0, w*h*4, imageBuffer);
-
-    // no fast path? go slow.  We sadly need this for now, instead of just
-    // throwing, because dataArray might not be dense in case someone stuck
-    // their own array on the imageData.
-    // FIXME: it'd be awfully nice if we could prevent such modification of
-    // imageData objects, since it's likely the spec won't allow it anyway.
-    // Bug 497110 covers this.
-    if (!canFastPath) {
-        jsval vr, vg, vb, va;
-        PRUint8 ir, ig, ib, ia;
-        for (int32 j = 0; j < h; j++) {
-            int32 lineOffset = (j*w*4);
-            for (int32 i = 0; i < w; i++) {
-                int32 pixelOffset = lineOffset + i*4;
-                if (!JS_GetElement(ctx, dataArray, pixelOffset + 0, &vr) ||
-                    !JS_GetElement(ctx, dataArray, pixelOffset + 1, &vg) ||
-                    !JS_GetElement(ctx, dataArray, pixelOffset + 2, &vb) ||
-                    !JS_GetElement(ctx, dataArray, pixelOffset + 3, &va))
-                    return NS_ERROR_DOM_SYNTAX_ERR;
-
-                if (JSVAL_IS_INT(vr))         ir = ToUint8(JSVAL_TO_INT(vr));
-                else if (JSVAL_IS_DOUBLE(vr)) ir = ToUint8(*JSVAL_TO_DOUBLE(vr));
-                else return NS_ERROR_DOM_SYNTAX_ERR;
-
-                if (JSVAL_IS_INT(vg))         ig = ToUint8(JSVAL_TO_INT(vg));
-                else if (JSVAL_IS_DOUBLE(vg)) ig = ToUint8(*JSVAL_TO_DOUBLE(vg));
-                else return NS_ERROR_DOM_SYNTAX_ERR;
-
-                if (JSVAL_IS_INT(vb))         ib = ToUint8(JSVAL_TO_INT(vb));
-                else if (JSVAL_IS_DOUBLE(vb)) ib = ToUint8(*JSVAL_TO_DOUBLE(vb));
-                else return NS_ERROR_DOM_SYNTAX_ERR;
-
-                if (JSVAL_IS_INT(va))         ia = ToUint8(JSVAL_TO_INT(va));
-                else if (JSVAL_IS_DOUBLE(va)) ia = ToUint8(*JSVAL_TO_DOUBLE(va));
-                else return NS_ERROR_DOM_SYNTAX_ERR;
-
-                // Convert to premultiplied color (losslessly if the input came from getImageData)
-                ir = sPremultiplyTable[ia][ir];
-                ig = sPremultiplyTable[ia][ig];
-                ib = sPremultiplyTable[ia][ib];
-
-#ifdef IS_LITTLE_ENDIAN
-                *imgPtr++ = ib;
-                *imgPtr++ = ig;
-                *imgPtr++ = ir;
-                *imgPtr++ = ia;
-#else
-                *imgPtr++ = ia;
-                *imgPtr++ = ir;
-                *imgPtr++ = ig;
-                *imgPtr++ = ib;
-#endif
-            }
-        }
-    } else {
-        /* Walk through and premultiply and swap rgba */
-        PRUint8 ir, ig, ib, ia;
-        PRUint8 *ptr = imgPtr;
-        for (int32 i = 0; i < w*h; i++) {
-            ir = ptr[0];
-            ig = ptr[1];
-            ib = ptr[2];
-            ia = ptr[3];
-
-#ifdef IS_LITTLE_ENDIAN
-            ptr[0] = sPremultiplyTable[ia][ib];
-            ptr[1] = sPremultiplyTable[ia][ig];
-            ptr[2] = sPremultiplyTable[ia][ir];
-#else
-            ptr[0] = ia;
-            ptr[1] = sPremultiplyTable[ia][ir];
-            ptr[2] = sPremultiplyTable[ia][ig];
-            ptr[3] = sPremultiplyTable[ia][ib];
-#endif
-            ptr += 4;
-        }
-    }
-
-    nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(imageBuffer.get(),
-                                                            gfxIntSize(w, h),
-                                                            w * 4,
+    nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
                                                             gfxASurface::ImageFormatARGB32);
     if (!imgsurf || imgsurf->CairoStatus())
         return NS_ERROR_FAILURE;
 
+    // ensure premultiply table has been created
+    EnsurePremultiplyTable();
+
+    PRUint8 *src = aData;
+    PRUint8 *dst = imgsurf->Data();
+
+    for (int j = 0; j < h; j++) {
+        for (int i = 0; i < w; i++) {
+            PRUint8 r = *src++;
+            PRUint8 g = *src++;
+            PRUint8 b = *src++;
+            PRUint8 a = *src++;
+
+            // Convert to premultiplied color (losslessly if the input came from getImageData)
+#ifdef IS_LITTLE_ENDIAN
+            *dst++ = sPremultiplyTable[a][b];
+            *dst++ = sPremultiplyTable[a][g];
+            *dst++ = sPremultiplyTable[a][r];
+            *dst++ = a;
+#else
+            *dst++ = a;
+            *dst++ = sPremultiplyTable[a][r];
+            *dst++ = sPremultiplyTable[a][g];
+            *dst++ = sPremultiplyTable[a][b];
+#endif
+        }
+    }
+
     gfxContextPathAutoSaveRestore pathSR(mThebes);
     gfxContextAutoSaveRestore autoSR(mThebes);
 
     // ignore clipping region, as per spec
     mThebes->ResetClip();
 
     mThebes->IdentityMatrix();
     mThebes->Translate(gfxPoint(x, y));
     mThebes->NewPath();
     mThebes->Rectangle(gfxRect(0, 0, w, h));
     mThebes->SetSource(imgsurf, gfxPoint(0, 0));
     mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
     mThebes->Fill();
 
-    return Redraw();
+    return Redraw(gfxRect(x, y, w, h));
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
 {
     if (!mSurface) {
         *surface = nsnull;
         return NS_ERROR_NOT_AVAILABLE;
@@ -3790,89 +3607,18 @@ nsCanvasRenderingContext2D::GetThebesSur
     NS_ADDREF(*surface);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::CreateImageData()
 {
-    if (!mValid)
-        return NS_ERROR_FAILURE;
-
-    nsAXPCNativeCallContext *ncc = nsnull;
-    nsresult rv = nsContentUtils::XPConnect()->
-        GetCurrentNativeCallContext(&ncc);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!ncc)
-        return NS_ERROR_FAILURE;
-
-    JSContext *ctx = nsnull;
-
-    rv = ncc->GetJSContext(&ctx);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRUint32 argc;
-    jsval *argv = nsnull;
-
-    ncc->GetArgc(&argc);
-    ncc->GetArgvPtr(&argv);
-
-    JSAutoRequest ar(ctx);
-
-    int32 width, height;
-    if (!JS_ConvertArguments (ctx, argc, argv, "jj", &width, &height))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    if (width <= 0 || height <= 0)
-        return NS_ERROR_DOM_INDEX_SIZE_ERR;
-
-    PRUint32 w = (PRUint32) width;
-    PRUint32 h = (PRUint32) height;
-
-    // check for overflow when calculating len
-    PRUint32 len0 = w * h;
-    if (len0 / w != (PRUint32) h)
-        return NS_ERROR_DOM_INDEX_SIZE_ERR;
-    PRUint32 len = len0 * 4;
-    if (len / 4 != len0)
-        return NS_ERROR_DOM_INDEX_SIZE_ERR;
-
-    jsval *dest;
-    JSObject *dataArray = js_NewArrayObjectWithCapacity(ctx, len, &dest);
-    if (!dataArray)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRUint32 i = 0; i < len; i++)
-        *dest++ = JSVAL_ZERO;
-
-    // Allocate result object after array, so if we have to trigger gc
-    // we do it now.
-    JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL);
-    if (!result)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsAutoGCRoot resultGCRoot(&result, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) ||
-        !JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) ||
-        !JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0))
-        return NS_ERROR_FAILURE;
-
-    jsval *retvalPtr;
-    ncc->GetRetValPtr(&retvalPtr);
-    *retvalPtr = OBJECT_TO_JSVAL(result);
-    ncc->SetReturnValueWasSet(PR_TRUE);
-
-    return NS_OK;
+    /* Should never be called; handled entirely in the quickstub */
+    return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetMozImageSmoothingEnabled(PRBool *retVal)
 {
     *retVal = CurrentState().imageSmoothingEnabled;
     return NS_OK;
 }
--- a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
+++ b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
@@ -177,20 +177,32 @@ enum CanvasMultiGetterType {
   // point-membership test
   boolean isPointInPath(in float x, in float y);
 
   // pixel manipulation
   // ImageData getImageData (in float x, in float y, in float width, in float height);
   // void putImageData (in ImageData d, in float x, in float y);
   // ImageData = { width: #, height: #, data: [r, g, b, a, ...] }
 
+  // These are just dummy functions; for JS, they are implemented as quickstubs
+  // that call the _explicit methods below.  Native callers should use the _explit
+  // methods directly.
   void getImageData();
   void putImageData();
 
+  // dataLen must be == width*height*4 in both of these calls
+  [noscript] void getImageData_explicit(in long x, in long y, in unsigned long width, in unsigned long height,
+                                        [array, size_is(dataLen)] in octet dataPtr, in unsigned long dataLen);
+  [noscript] void putImageData_explicit(in long x, in long y, in unsigned long width, in unsigned long height,
+                                        [array, size_is(dataLen)] in octet dataPtr, in unsigned long dataLen);
+
   // ImageData createImageData(in float w, in float h);
+  // Note: this is basically script-only (and really, quickstub-only).  Native callers
+  // should just use the noscript 'explicit' get/put methods above, instead of using
+  // a separate ImageData object.
   void createImageData();
 
   // image smoothing mode -- if disabled, images won't be smoothed
   // if scaled.
   attribute boolean mozImageSmoothingEnabled;
 
   // Show the caret if appropriate when drawing
   const unsigned long DRAWWINDOW_DRAW_CARET   = 0x01;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -12084,16 +12084,17 @@ TraceRecorder::setElem(int lval_spindex,
                     break;
                   case js::TypedArray::TYPE_FLOAT64:
                     addr_ins = lir->ins2(LIR_piadd, data_ins, lir->ins2i(LIR_pilsh, pidx_ins, 3));
                     lir->insStore(LIR_stfi, v_ins, addr_ins, 0);
                     break;
                   case js::TypedArray::TYPE_UINT8_CLAMPED:
                     addr_ins = lir->ins2(LIR_piadd, data_ins, pidx_ins);
                     lir->insStore(LIR_stb, lir->insCall(&js_TypedArray_uint8_clamp_double_ci, &v_ins), addr_ins, 0);
+                    break;
                   default:
                     JS_NOT_REACHED("Unknown typed array type in tracer");
                 }
             }
         } else {
             RETURN_STOP_A("can't trace setting typed array element to non-number value");
         }
     } else if (JSVAL_TO_INT(idx) < 0 || !OBJ_IS_DENSE_ARRAY(cx, obj)) {
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -515,19 +515,28 @@ class TypedArrayTemplate
         if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
             *vp = INT_TO_JSVAL(tarray->length);
             return true;
         }
 
         jsuint index;
         // We can't just chain to js_SetProperty, because we're not a normal object.
         if (!tarray->isArrayIndex(cx, id, &index)) {
+#if 0
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_TYPED_ARRAY_BAD_INDEX);
             return false;
+#endif
+            // Silent ignore is better than an exception here, because
+            // at some point we may want to support other properties on
+            // these objects.  This is especially true when these arrays
+            // are used to implement HTML Canvas 2D's PixelArray objects,
+            // which used to be plain old arrays.
+            *vp = JSVAL_VOID;
+            return true;
         }
 
         if (JSVAL_IS_INT(*vp)) {
             tarray->setIndex(index, NativeType(JSVAL_TO_INT(*vp)));
         } else if (JSVAL_IS_DOUBLE(*vp)) {
             tarray->setIndex(index, NativeType(*JSVAL_TO_DOUBLE(*vp)));
         } else {
             jsdouble d;
--- a/js/src/tests/js1_8_5/extensions/typedarray.js
+++ b/js/src/tests/js1_8_5/extensions/typedarray.js
@@ -171,19 +171,20 @@ function test()
     check(function() a[1] == 0xbb);
 
     // not sure if this is supposed to throw or to treat "foo"" as 0.
     checkThrows(function() new Int32Array([0xaa, "foo", 0xbb]), TODO);
 
     checkThrows(function() new Int32Array(-100));
 
     a = new Uint8Array(3);
-    checkThrows(function() a[5000] = 0);
-    checkThrows(function() a["hello"] = 0);
-    checkThrows(function() a[-10] = 0);
+    // XXX these are ignored now and return undefined
+    //checkThrows(function() a[5000] = 0);
+    //checkThrows(function() a["hello"] = 0);
+    //checkThrows(function() a[-10] = 0);
     check(function() (a[0] = "10") && (a[0] == 10));
 
 
     // check Uint8ClampedArray, which is an extension to this extension
     a = new Uint8ClampedArray(4);
     a[0] = 128;
     a[1] = 512;
     a[2] = -123.723;
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -87,21 +87,16 @@ members = [
 
     # dom/interfaces/canvas
     #
     # nsIDOMCanvasRenderingContext2D and friends
     'nsIDOMCanvasRenderingContext2D.*',
     'nsIDOMTextMetrics.*',
     'nsIDOMCanvasGradient.*',
     'nsIDOMCanvasPattern.*',
-    # NOTE: createImageDate(), getImageData(), and putImageData() use
-    #       GetCurrentNativeCallContext
-    '-nsIDOMCanvasRenderingContext2D.createImageData',
-    '-nsIDOMCanvasRenderingContext2D.getImageData',
-    '-nsIDOMCanvasRenderingContext2D.putImageData',
 
     # dom/interfaces/core
     'nsIDOMCharacterData.data',
     'nsIDOMCharacterData.length',
     'nsIDOMDocument.documentElement',
     'nsIDOMDocument.implementation',
     'nsIDOMDocument.getElementsByTagName',
     'nsIDOMDocument.doctype',
@@ -620,11 +615,15 @@ customMethodCalls = {
     'nsICanvasRenderingContextWebGL_Uniform3fv': CUSTOM_QS_TN,
     'nsICanvasRenderingContextWebGL_Uniform4fv': CUSTOM_QS_TN,
     'nsICanvasRenderingContextWebGL_UniformMatrix2fv': CUSTOM_QS_TN,
     'nsICanvasRenderingContextWebGL_UniformMatrix3fv': CUSTOM_QS_TN,
     'nsICanvasRenderingContextWebGL_UniformMatrix4fv': CUSTOM_QS_TN,
     'nsICanvasRenderingContextWebGL_VertexAttrib1fv': CUSTOM_QS,
     'nsICanvasRenderingContextWebGL_VertexAttrib2fv': CUSTOM_QS,
     'nsICanvasRenderingContextWebGL_VertexAttrib3fv': CUSTOM_QS,
-    'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS
+    'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS,
+    # Canvas 2D
+    'nsIDOMCanvasRenderingContext2D_CreateImageData': CUSTOM_QS,
+    'nsIDOMCanvasRenderingContext2D_GetImageData': CUSTOM_QS,
+    'nsIDOMCanvasRenderingContext2D_PutImageData': CUSTOM_QS,
     }