Bug 1044102 - Part 0 - Test cases. r=smaug.
authorKaku Kuo <tkuo@mozilla.com>
Thu, 30 Jul 2015 20:45:00 +0200
changeset 287301 9b8ade9cdad7e45d3ff2c86f953710362e4ea24c
parent 287300 ebca2b984bd4ac3a431db4acee101721427ab633
child 287302 af88cf4b922b678b8de4a8e9df33d15b4b828c03
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1044102
milestone42.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 1044102 - Part 0 - Test cases. r=smaug.
dom/canvas/test/image_error-early.png
dom/canvas/test/imagebitmap_on_worker.js
dom/canvas/test/imagebitmap_structuredclone.js
dom/canvas/test/imagebitmap_structuredclone_iframe.html
dom/canvas/test/mochitest.ini
dom/canvas/test/test_imagebitmap.html
dom/canvas/test/test_imagebitmap_on_worker.html
dom/canvas/test/test_imagebitmap_structuredclone.html
dom/canvas/test/test_imagebitmap_structuredclone_iframe.html
dom/canvas/test/test_imagebitmap_structuredclone_window.html
dom/tests/mochitest/general/test_interfaces.html
dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
dom/workers/test/test_worker_interfaces.js
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/image_error-early.png
@@ -0,0 +1,1 @@
+ERROR
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_on_worker.js
@@ -0,0 +1,81 @@
+function ok(expect, msg) {
+  postMessage({type: "status", status: !!expect, msg: msg});
+}
+
+function doneTask() {
+  postMessage({type: "doneTask"});
+}
+
+function promiseThrows(p, name) {
+  var didThrow;
+  return p.then(function() { didThrow = false; },
+                function() { didThrow = true; })
+          .then(function() { ok(didThrow, "[TestException] " + name); });
+}
+
+onmessage = function(event) {
+  if (event.data.type == "testImageData") {
+    var width = event.data.width;
+    var height = event.data.height;
+    var imageData = event.data.source;
+    ok(imageData, "[CreateFromImageData] An ImageData is passed into worker.")
+    ok(imageData.width == width, "[CreateFromImageData] Passed ImageData has right width = " + width);
+    ok(imageData.height == height, "[CreateFromImageData] Passed ImageData has right height = " + height);
+
+    var promise = createImageBitmap(imageData);
+    promise.then(function(bitmap) {
+      ok(bitmap, "[CreateFromImageData] ImageBitmap is created successfully.");
+      ok(bitmap.width == width, "[CreateFromImageData] ImageBitmap.width = " + bitmap.width + ", expected witdth = " + width);
+      ok(bitmap.height == height, "[CreateFromImageData] ImageBitmap.height = " + bitmap.height + ", expected height = " + height);
+
+      doneTask();
+    });
+  } else if (event.data.type == "testBlob") {
+    var width = event.data.width;
+    var height = event.data.height;
+    var blob = event.data.source;
+    ok(blob, "[CreateFromBlob] A Blob object is passed into worker.");
+
+    var promise = createImageBitmap(blob);
+    promise.then(function(bitmap) {
+      ok(bitmap, "[CreateFromBlob] ImageBitmap is created successfully.");
+      ok(bitmap.width == width, "[CreateFromBlob] ImageBitmap.width = " + bitmap.width + ", expected witdth = " + width);
+      ok(bitmap.height == height, "[CreateFromBlob] ImageBitmap.height = " + bitmap.height + ", expected height = " + height);
+
+      doneTask();
+    });
+  } else if (event.data.type == "testImageBitmap") {
+    var width  = event.data.width;
+    var height = event.data.height;
+    var source = event.data.source;
+    ok(source, "[CreateFromImageBitmap] A soruce object is passed into worker.");
+
+    var promise = createImageBitmap(source);
+    promise.then(function(bitmap) {
+      ok(bitmap, "[CreateFromImageBitmap] ImageBitmap is created successfully.");
+      ok(bitmap.width == width, "[CreateFromImageBitmap] ImageBitmap.width = " + bitmap.width + ", expected witdth = " + width);
+      ok(bitmap.height == height, "[CreateFromImageBitmap] ImageBitmap.height = " + bitmap.height + ", expected height = " + height);
+
+      var promise2 = createImageBitmap(bitmap);
+      promise2.then(function(bitmap2) {
+        ok(bitmap2, "[CreateFromImageBitmap] 2nd ImageBitmap is created successfully.");
+        ok(bitmap.width == width, "[CreateFromImageBitmap] ImageBitmap.width = " + bitmap.width + ", expected witdth = " + width);
+        ok(bitmap.height == height, "[CreateFromImageBitmap] ImageBitmap.height = " + bitmap.height + ", expected height = " + height);
+
+        doneTask();
+      });
+    });
+  } else if (event.data.type == "testException") {
+    var source = event.data.source;
+    if (event.data.sx) {
+      var sx = event.data.sx;
+      var sy = event.data.sy;
+      var sw = event.data.sw;
+      var sh = event.data.sh;
+      promiseThrows(createImageBitmap(source, sx, sy, sw, sh), event.data.msg);
+    } else {
+      promiseThrows(createImageBitmap(source), event.data.msg);
+    }
+    doneTask();
+  }
+};
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_structuredclone.js
@@ -0,0 +1,30 @@
+function ok(expect, msg) {
+  postMessage({"type": "status", status: !!expect, msg: msg});
+}
+
+onmessage = function(event) {
+  ok(!!event.data.bitmap1, "Get the 1st ImageBitmap from the main script.");
+  ok(!!event.data.bitmap2, "Get the 2st ImageBitmap from the main script.");
+
+  // send the first original ImageBitmap back to the main-thread
+  postMessage({"type":"bitmap1",
+               "bitmap":event.data.bitmap1});
+
+  // create a new ImageBitmap from the 2nd original ImageBitmap
+  // and then send the newly created ImageBitmap back to the main-thread
+  var promise = createImageBitmap(event.data.bitmap2);
+  promise.then(
+    function(bitmap) {
+      ok(true, "Successfully create a new ImageBitmap from the 2nd original bitmap in worker.");
+
+      // send the newly created ImageBitmap back to the main-thread
+      postMessage({"type":"bitmap2", "bitmap":bitmap});
+
+      // finish the test
+      postMessage({"type": "finish"});
+    },
+    function() {
+      ok(false, "Cannot create a new bitmap from the original bitmap in worker.");
+    }
+  );
+}
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_structuredclone_iframe.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<body>
+  <script type="application/javascript">
+
+  function ok(expect, msg) {
+    window.parent.postMessage({"type": "status", status: !!expect, msg: msg}, "*");
+  }
+
+  window.onmessage = function(event) {
+    ok(!!event.data.bitmap1, "Get the 1st ImageBitmap from the main script.");
+    ok(!!event.data.bitmap2, "Get the 2st ImageBitmap from the main script.");
+
+    // send the first original ImageBitmap back to the main window
+    window.parent.postMessage({"type":"bitmap1", "bitmap":event.data.bitmap1}, "*");
+
+    // create a new ImageBitmap from the 2nd original ImageBitmap
+    // and then send the newly created ImageBitmap back to the main window
+    var promise = createImageBitmap(event.data.bitmap2);
+    promise.then(
+      function(bitmap) {
+        ok(true, "Successfully create a new ImageBitmap from the 2nd original bitmap in worker.");
+
+        // send the newly created ImageBitmap back to the main window
+        window.parent.postMessage({"type":"bitmap2", "bitmap":bitmap}, "*");
+
+        // finish the test
+        window.parent.postMessage({"type": "finish"}, "*");
+      },
+      function() {
+        ok(false, "Cannot create a new bitmap from the original bitmap in worker.");
+      }
+    );
+  }
+
+
+  </script>
+</body>
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 support-files =
   android.json
   file_drawImage_document_domain.html
   image_anim-gr.gif
   image_anim-gr.png
   image_anim-poster-gr.png
   image_broken.png
+  image_error-early.png
   image_ggrr-256x256.png
   image_green-16x16.png
   image_green-1x1.png
   image_green-redirect
   image_green-redirect^headers^
   image_green.png
   image_red-16x16.png
   image_red.png
@@ -18,16 +19,19 @@ support-files =
   image_red_crossorigin_credentials.png^headers^
   image_redtransparent.png
   image_rgrg-256x256.png
   image_rrgg-256x256.png
   image_transparent.png
   image_transparent50.png
   image_yellow.png
   image_yellow75.png
+  imagebitmap_on_worker.js
+  imagebitmap_structuredclone.js
+  imagebitmap_structuredclone_iframe.html
 
 [test_2d.clearRect.image.offscreen.html]
 [test_2d.clip.winding.html]
 [test_2d.composite.canvas.color-burn.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
 [test_2d.composite.canvas.color-dodge.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
 [test_2d.composite.canvas.color.html]
@@ -229,16 +233,21 @@ skip-if = os == "android" || appname == 
 [test_capture.html]
 support-files = captureStream_common.js
 [test_drawImageIncomplete.html]
 [test_drawImage_document_domain.html]
 [test_drawImage_edge_cases.html]
 [test_drawWindow.html]
 support-files = file_drawWindow_source.html file_drawWindow_common.js
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk')
+[test_imagebitmap.html]
+[test_imagebitmap_on_worker.html]
+[test_imagebitmap_structuredclone.html]
+[test_imagebitmap_structuredclone_iframe.html]
+[test_imagebitmap_structuredclone_window.html]
 [test_ImageData_ctor.html]
 [test_isPointInStroke.html]
 [test_mozDashOffset.html]
 [test_mozGetAsFile.html]
 [test_strokeText_throw.html]
 [test_toBlob.html]
 [test_toDataURL_alpha.html]
 [test_toDataURL_lowercase_ascii.html]
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap.html
@@ -0,0 +1,342 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+
+<img src="image_anim-gr.gif" id="image" class="resource">
+<video width="320" height="240" src="http://example.com/tests/dom/canvas/test/crossorigin/video.sjs?name=tests/dom/media/test/320x240.ogv&type=video/ogg&cors=anonymous" id="video" crossOrigin="anonymous" autoplay></video>
+
+<canvas id="c1" class="output" width="128" height="128"></canvas>
+<canvas id="c2" width="128" height="128"></canvas>
+
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/**
+ * [isPixel description]
+ * @param  {[type]}  ctx : canvas context
+ * @param  {[type]}  x   : pixel x coordinate
+ * @param  {[type]}  y   : pixel y coordinate
+ * @param  {[type]}  c   : a rgba color code
+ * @param  {[type]}  d   : error duration
+ * @return {Promise}
+ */
+function isPixel(ctx, x, y, c, d) {
+  var pos = x + "," + y;
+  var color = c[0] + "," + c[1] + "," + c[2] + "," + c[3];
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var pr = pixel.data[0],
+      pg = pixel.data[1],
+      pb = pixel.data[2],
+      pa = pixel.data[3];
+  ok(c[0]-d <= pr && pr <= c[0]+d &&
+     c[1]-d <= pg && pg <= c[1]+d &&
+     c[2]-d <= pb && pb <= c[2]+d &&
+     c[3]-d <= pa && pa <= c[3]+d,
+     "pixel "+pos+" of "+ctx.canvas.id+" is "+pr+","+pg+","+pb+","+pa+"; expected "+color+" +/- "+d);
+}
+
+var TEST_BITMAPS = [
+    {'rect': [0, 0, 128, 128],        'draw': [0, 0, 64, 64, 0, 0, 64, 64],       'test': [[0,    0,    [255, 0, 0, 255], 5]]},
+    {'rect': [128, 0, 128, 128],      'draw': [0, 0, 64, 64, 0, 0, 64, 64],       'test': [[0,    0,    [0, 255, 0, 255], 5]]},
+    {'rect': [230, 230, 128, 128],    'draw': [0, 0, 128, 128, 0, 0, 128, 128],   'test': [[0,    0,    [255, 0, 0, 255], 5],
+                                                                                           [100,  100,  [0, 0, 0, 0],     5]]},
+    {'rect': [-64, -64, 512, 512],    'draw': [0, 0, 128, 128, 0, 0, 128, 128],   'test': [[0,    0,    [0, 0, 0, 0],     5],
+                                                                                           [100,  100,  [255, 0, 0, 255], 5]]},
+    {'rect': [128, 128, -128, -128],  'draw': [0, 0, 128, 128, 0, 0, 128, 128],   'test': [[0,    0,    [255, 0, 0, 255], 5]]},
+    {'rect': [0, 0, 256, 256],        'draw': [0, 128, 128, 128, 0, 0, 128, 128], 'test': [[0,    0,    [0, 255, 0, 255], 5]]},
+];
+
+var canvas, ctx, ctx2, completedImage;
+
+function failed(ex) {
+  ok(false, "Promise failure: " + ex);
+}
+
+function testDraw() {
+  var resolver, bitmaps = [], image = new Image();
+
+  image.src = 'image_rgrg-256x256.png';
+  var promise = new Promise(function (arg) { resolver = arg; });
+
+  function createBitmap(def) {
+    return createImageBitmap(image, def.rect[0], def.rect[1], def.rect[2], def.rect[3])
+      .then(function (bitmap) { def.bitmap = bitmap; }, failed);
+  };
+
+  image.onload = function() {
+    completedImage = image;
+    resolver(Promise.all(TEST_BITMAPS.map(createBitmap)));
+  };
+
+  function testPixel(test) {
+    isPixel(ctx, test[0], test[1], test[2], test[3]);
+  };
+
+  return promise.then(function() {
+    TEST_BITMAPS.forEach(function (test) {
+      if (!test.bitmap) { return; }
+      ctx.clearRect(0, 0, canvas.width, canvas.height);
+      ctx.drawImage(test.bitmap, test.draw[0], test.draw[1], test.draw[2], test.draw[3], test.draw[4], test.draw[5], test.draw[6], test.draw[7]);
+      test.test.forEach(testPixel);
+      is(test.bitmap.width, Math.abs(test.rect[2]), "Bitmap has correct width " + test.bitmap.width);
+      is(test.bitmap.height, Math.abs(test.rect[3]), "Bitmap has correct height " + test.bitmap.height);
+    });
+  });
+}
+
+function testSources() {
+  ctx.fillStyle="#00FF00";
+  ctx.fillRect(0, 0, 128, 128);
+
+  function check(bitmap) {
+    ctx2.clearRect(0, 0, 128, 128);
+    ctx2.drawImage(bitmap, 0, 0);
+    isPixel(ctx2, 0, 0, [0, 255, 0, 255], 5);
+  };
+
+  function getPNGBlobBitmapPromise() {
+    return new Promise(function(resolve, reject) {
+      canvas.toBlob(function(blob) {
+        resolve(createImageBitmap(blob));
+      })
+    });
+  }
+
+  function getJPGBlobBitmapPromise() {
+    return new Promise(function(resolve, reject) {
+      canvas.toBlob(function(blob) {
+        resolve(createImageBitmap(blob));
+      }, "image/jpeg", 0.95)
+    });
+  }
+
+  return Promise.all([
+    createImageBitmap(document.getElementById('image')).then(check, failed),                                         // HTMLImageElement
+    createImageBitmap(ctx).then(check, failed),                                                                      // CanvasRenderingContext2D
+    createImageBitmap(canvas).then(check, failed),                                                                   // HTMLCanvasElement
+    createImageBitmap(ctx).then(function (bitmap) { return createImageBitmap(bitmap).then(check, failed); }, failed), // ImageBitmap
+    createImageBitmap(document.getElementById('video'), 140, 0, 20, 20).then(check, failed),                         // HTMLVideoElement
+    createImageBitmap(ctx.getImageData(0, 0, 128, 128)).then(check, failed),                                          // ImageData
+    getPNGBlobBitmapPromise().then(check, failed),                                                                   // PNG blob
+    getJPGBlobBitmapPromise().then(check, failed),                                                                   // JPEG blob
+  ]);
+}
+
+function promiseThrows(p, name) {
+  var didThrow;
+  return p.then(function() { didThrow = false; },
+                function() { didThrow = true; })
+          .then(function() { ok(didThrow, name); });
+}
+
+function testExceptions() {
+
+  function createImageBitmapWithNeuturedImageData() {
+    return new Promise(function(resolve, reject) {
+      var tempImage = document.createElement('img');
+      tempImage.src = 'image_rgrg-256x256.png';
+      tempImage.onload = function() {
+        var tempCanvas = document.createElement('canvas');
+        var tempCtx = tempCanvas.getContext('2d');
+        tempCanvas.with = tempImage.naturalWidth;
+        tempCanvas.height = tempImage.naturalHeight;
+        tempCtx.drawImage(tempImage, 0, 0);
+        var tempWorker = new Worker("test_imagebitmap_on_worker.js");
+        var imageData = tempCtx.getImageData(0, 0, tempImage.naturalWidth, tempImage.naturalHeight);
+        tempWorker.postMessage(imageData.data.buffer, [imageData.data.buffer]);
+        tempWorker.terminate();
+
+        ok(imageData.data.length == 0, "Get a neutured ImageData.");
+        resolve(createImageBitmap(imageData));
+      }
+    });
+  }
+
+  function createImageBitmapWithCorruptedBlob() {
+    return new Promise(function(resolve, reject) {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", "image_error-early.png");
+      xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
+      xhr.onload = function()
+      {
+        ok(xhr.response, "Get a corrupted blob");
+        resolve(createImageBitmap(xhr.response));
+      }
+      xhr.send();
+    });
+  }
+
+  function createImageBitmapWithNonImageFile() {
+    return new Promise(function(resolve, reject) {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", "test_imagebitmap_on_worker.js");
+      xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
+      xhr.onload = function()
+      {
+        ok(xhr.response, "Get a non-image blob");
+        resolve(createImageBitmap(xhr.response));
+      }
+      xhr.send();
+    });
+  }
+
+  return Promise.all([
+    promiseThrows(createImageBitmap(new Image()), 'createImageBitmap should throw with unloaded image'),
+    promiseThrows(createImageBitmap(completedImage, 0, 0, 0, 0), 'createImageBitmap should throw with 0 width/height'),
+    promiseThrows(createImageBitmap(null), 'createImageBitmap should throw with null source'),
+    promiseThrows(createImageBitmapWithNeuturedImageData(), "createImageBitmap should throw with neutured ImageData"),
+    promiseThrows(createImageBitmapWithCorruptedBlob(), "createImageBitmap should throw with corrupted blob"),
+    promiseThrows(createImageBitmapWithNonImageFile(), "createImageBitmap should throw with non-image blob"),
+  ]);
+}
+
+function testSecurityErrors() {
+
+  function getUncleanImagePromise() {
+    return new Promise(function(resolve, reject) {
+      var uncleanImage = document.createElement('img');
+
+      uncleanImage.onload = function() {
+        resolve(createImageBitmap(uncleanImage));
+      }
+
+      uncleanImage.onerror = function() {
+        reject();
+      }
+
+      uncleanImage.src = "http://example.com/tests/dom/canvas/test/crossorigin/image.png";
+    });
+  }
+
+  function getUncleanVideoPromise() {
+    return new Promise(function(resolve, reject) {
+      var uncleanVideo = document.createElement('video');
+
+      uncleanVideo.onloadeddata = function() {
+        resolve(createImageBitmap(uncleanVideo));
+      }
+
+      uncleanVideo.onerror = function() {
+        reject();
+      }
+
+      uncleanVideo.src = "http://example.com/tests/dom/canvas/test/crossorigin/video.sjs?name=tests/dom/media/test/320x240.ogv&type=video/ogg";
+      uncleanVideo.play();
+    });
+  }
+
+  function getTaintedCanvasPromise() {
+    return new Promise(function(resolve, reject) {
+      var uncleanImage = document.createElement('img');
+
+      uncleanImage.onload = function() {
+        var taintedCanvas = document.createElement('canvas');
+        var taintedCtx = taintedCanvas.getContext('2d');
+        taintedCtx.drawImage(uncleanImage, 0, 0);
+        resolve(createImageBitmap(taintedCanvas));
+      }
+
+      uncleanImage.onerror = function() {
+        reject();
+      }
+
+      uncleanImage.src = "http://example.com/tests/dom/canvas/test/crossorigin/image.png";
+    });
+  }
+
+  function getTaintedCanvasRenderingContex2dPromise() {
+    return new Promise(function(resolve, reject) {
+      var uncleanImage = document.createElement('img');
+
+      uncleanImage.onload = function() {
+        var taintedCanvas = document.createElement('canvas');
+        var taintedCtx = taintedCanvas.getContext('2d');
+        taintedCtx.drawImage(uncleanImage, 0, 0);
+        resolve(createImageBitmap(taintedCtx));
+      }
+
+      uncleanImage.onerror = function() {
+        reject();
+      }
+
+      uncleanImage.src = "http://example.com/tests/dom/canvas/test/crossorigin/image.png";
+    });
+  }
+
+  function checkPromiseFailedWithSecurityError(p) {
+    return p.then( function(reason) { ok(false, "Did not get SecurityError with unclean source. ImageBitmap was created successfully."); },
+                   function(reason) { if (reason == "SecurityError: The operation is insecure.") {
+                                        ok(true, reason);
+                                      }
+                                      else {
+                                        ok(false, "Did not get SecurityError with unclean source. Error Message: " + reason);
+                                      }});
+  }
+
+  return Promise.all([
+    checkPromiseFailedWithSecurityError(getUncleanImagePromise()),
+    checkPromiseFailedWithSecurityError(getUncleanVideoPromise()),
+    checkPromiseFailedWithSecurityError(getTaintedCanvasPromise()),
+    checkPromiseFailedWithSecurityError(getTaintedCanvasRenderingContex2dPromise()),
+  ]);
+}
+
+function testCreatePattern() {
+  var resolve;
+  var promise = new Promise(function (arg) { resolve = arg; });
+
+  var TEST_PATTERN = [
+    [0,   0,   [255, 0, 0, 255], 1],
+    [128, 128, [255, 0, 0, 255], 1],
+    [256, 256, [255, 0, 0, 255], 1],
+    [384, 0,   [0, 255, 0, 255], 1],
+    [0,   384, [0, 255, 0, 255], 1],
+  ];
+
+  var canvas = document.createElement('canvas');
+  canvas.width = "512";
+  canvas.height = "512";
+  var ctx = canvas.getContext('2d');
+
+  var image = new Image();
+  image.src = 'image_rgrg-256x256.png';
+  image.onload = function() {
+    var p = createImageBitmap(image);
+    p.then(function(bitmap) {
+      ctx.rect(0, 0, 512, 512);
+      ctx.fillStyle = ctx.createPattern(bitmap, "repeat");
+      ctx.fill();
+      document.body.appendChild(canvas);
+    });
+    resolve(p);
+  }
+
+  return promise.then(function() {
+    TEST_PATTERN.forEach(function(test) {
+      isPixel(ctx, test[0], test[1], test[2], test[3]);
+    });
+  });
+}
+
+
+function runTests() {
+  canvas = document.getElementById('c1');
+  ctx = canvas.getContext('2d');
+  ctx2 = document.getElementById('c2').getContext('2d');
+
+  testDraw()
+    .then(testCreatePattern)
+    .then(testSources)
+    .then(testExceptions)
+    .then(testSecurityErrors)
+    .then(SimpleTest.finish, function(ev) { failed(ev); SimpleTest.finish(); });
+}
+
+addLoadEvent(runTests);
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_on_worker.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap on Worker</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<script type="text/javascript">
+
+// The following tests is not enabled in Worker now:
+// create from a HTMLImageElement
+// create from a HTMLVideoElement
+// create from a HTMLCanvasElement
+// create from a CanvasRenderingContext2D
+// call CanvasRenderingContext2D.drawImage()
+// call CanvasRenderingContext2D.createPaattern()
+// test security error from an unclean HTHMLImageElemnt
+// test security error from an unclean HTHMLVideoElemnt
+// test security error from an tainted HTHMLCanvasElemnt
+// test security error from an tainted CanvasRenderingContext2D
+
+// Task constructor function
+function Task(aType, aWidth, aHeight, aMsg, aSource) {
+  this.type = aType;
+  this.width = aWidth;
+  this.height = aHeight;
+  this.msg = aMsg;
+  this.source = aSource;
+}
+
+function TaskWithCrop(aType, aWidth, aHeight, aMsg, aSource, aSx, aSy, aSw, aSh) {
+  Task.call(this, aType, aWidth, aHeight, aMsg, aSource);
+  this.sx = aSx;
+  this.sy = aSy;
+  this.sw = aSw;
+  this.sh = aSh;
+}
+TaskWithCrop.prototype = Object.create(Task.prototype);
+TaskWithCrop.prototype.constructor = TaskWithCrop;
+
+var WORKER_TASKS = {
+  tasks: [], // an arrayf of Task objects
+  dispatch: function() {
+    if (this.tasks.length) {
+      worker.postMessage(this.tasks.pop());
+    } else {
+      worker.terminate();
+      SimpleTest.finish();
+    }
+  },
+};
+
+var worker = new Worker("imagebitmap_on_worker.js");
+worker.onmessage = function(event) {
+  if (event.data.type == "status") {
+    ok(event.data.status, event.data.msg);
+  } else if (event.data.type == "doneTask") {
+    WORKER_TASKS.dispatch();
+  }
+}
+
+function runTests() {
+  ok(worker, "Worker created successfully.");
+
+  // prepare an ImageData object
+  var image = document.createElement('img');
+  var canvas = document.createElement('canvas');
+  var ctx = canvas.getContext('2d');
+  var imageData;
+  image.src = "image_rgrg-256x256.png";
+  image.onload = function() {
+    var width = image.naturalWidth;
+    var height = image.naturalHeight;
+
+    canvas.width = image.naturalWidth;
+    canvas.height = image.naturalHeight;
+    ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight);
+
+    imageData = ctx.getImageData(0, 0, image.naturalWidth, image.naturalHeight);
+
+    // task: test soruce: an ImageData
+    WORKER_TASKS.tasks.push(new Task("testImageData", width, height, "", imageData));
+
+    // task: test soruce: an ImageBitmap
+    WORKER_TASKS.tasks.push(new Task("testImageBitmap", width, height, "", imageData));
+
+    // task: test soruce: a Blob
+    canvas.toBlob(function(aBlob) {
+    	WORKER_TASKS.tasks.push(new Task("testBlob", width, height, "", aBlob));
+    });
+  };
+
+  // task: throw exception: general: sw == 0 || sh == 0
+  WORKER_TASKS.tasks.push(new TaskWithCrop("testException", 0, 0, "createImageBitmap should throw with 0 width/height", imageData, 0, 0, 0, 0));
+
+  // task: throw exception: general: source is a null
+  WORKER_TASKS.tasks.push(new TaskWithCrop("testException", 0, 0, "createImageBitmap should throw with null source", null, 0, 0, 0, 0));
+
+  // task: throw exception: ImageData: an ImageData object whose data is data attribute has been neutered
+  var neuturedImageData = function getNeuturedImageData(imageData) {
+  	worker.postMessage(imageData.data.buffer, [imageData.data.buffer]);
+  	return imageData;
+  }(ctx.getImageData(0, 0, 50, 50));
+  WORKER_TASKS.tasks.push(new TaskWithCrop("testException", neuturedImageData.width, neuturedImageData.height,
+                                           "createImageBitmap should throw with neutured ImageData",
+                                           neuturedImageData, 0, 0, neuturedImageData.width, neuturedImageData.height));
+
+  // task: throw exception: Blob: a corrupted blob
+  function getCorruptedBlob(fileName) {
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", fileName);
+    xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
+    xhr.onload = function() {
+        WORKER_TASKS.tasks.push(new Task("testException", 0, 0, "createImageBitmap should reject promise with corrupted blob", xhr.response));
+    }
+    xhr.send();
+  }
+  getCorruptedBlob("image_error-early.png");
+
+  // task: throw exception: Blob: non-image file
+  function getNonImageFile(fileName) {
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", fileName);
+    xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
+    xhr.onload = function() {
+      WORKER_TASKS.tasks.push(new Task("testException", 0, 0, "createImageBitmap should reject promise with non-image blob", xhr.response));
+
+      // start to dispatch tasks to worker
+      WORKER_TASKS.dispatch();
+    }
+    xhr.send();
+  }
+  getNonImageFile("imagebitmap_on_worker.js");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_structuredclone.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap : Structured Clone</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<script type="text/javascript">
+
+var gImage1;
+var gImage2;
+var gImageBitmap1;
+var gImageBitmap2;
+
+function isPixel(ctx1, ctx2, x, y) {
+  var pixel1 = ctx1.getImageData(x, y, 1, 1);
+  var pixel2 = ctx2.getImageData(x, y, 1, 1);
+  ok(pixel1.data[0] == pixel2.data[0] &&
+     pixel1.data[1] == pixel2.data[1] &&
+     pixel1.data[2] == pixel2.data[2] &&
+     pixel1.data[3] == pixel2.data[3],
+    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
+}
+
+function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+
+  canvas1.width  = imageElement.naturalWidth;
+  canvas1.height = imageElement.naturalHeight;
+  canvas2.width  = imageElement.naturalWidth;
+  canvas2.height = imageElement.naturalHeight;
+
+  var ctx1 = canvas1.getContext('2d');
+  var ctx2 = canvas2.getContext('2d');
+
+  ctx1.drawImage(imageElement, 0, 0);
+  ctx2.drawImage(imageBitmap, 0, 0);
+
+  document.body.appendChild(canvas1);
+  document.body.appendChild(canvas2);
+
+  for (var t = 0; t < 20; ++t) {
+    // check one random pixel
+    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
+    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
+    isPixel(ctx1, ctx2, randomX, randomY);
+  }
+}
+
+var worker = new Worker("imagebitmap_structuredclone.js");
+worker.onmessage = function(event) {
+
+  if (event.data.type == "status") {
+    ok(event.data.status, event.data.msg);
+  } else if (event.data.type == "finish") {
+    SimpleTest.finish();
+  } else if (event.data.type == "bitmap1") {
+    compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
+  } else if (event.data.type == "bitmap2") {
+    compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
+  }
+}
+
+function prepareTwoImageBitmap() {
+  gImage1 = document.createElement('img');
+  gImage2 = document.createElement('img');
+  gImage1.src = "image_rgrg-256x256.png";
+  gImage2.src = "image_yellow.png";
+
+  var p1 = new Promise(function(resolve, reject) {
+    gImage1.onload = function() {
+      var promise = createImageBitmap(gImage1);
+      promise.then(function(bitmap) {
+        gImageBitmap1 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  var p2 = new Promise(function(resolve, reject) {
+    gImage2.onload = function() {
+      var promise = createImageBitmap(gImage2);
+      promise.then(function(bitmap) {
+        gImageBitmap2 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  return Promise.all([p1, p2]);
+}
+
+function runTests() {
+  ok(worker, "Worker created successfully.");
+
+  prepareTwoImageBitmap().then(function(){
+    worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2});
+  });
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_structuredclone_iframe.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap : StructuredClone between main window and iframe</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+
+var gImage1;
+var gImage2;
+var gImageBitmap1;
+var gImageBitmap2;
+
+function isPixel(ctx1, ctx2, x, y) {
+  var pixel1 = ctx1.getImageData(x, y, 1, 1);
+  var pixel2 = ctx2.getImageData(x, y, 1, 1);
+  ok(pixel1.data[0] == pixel2.data[0] &&
+     pixel1.data[1] == pixel2.data[1] &&
+     pixel1.data[2] == pixel2.data[2] &&
+     pixel1.data[3] == pixel2.data[3],
+    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
+}
+
+function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+
+  canvas1.width  = imageElement.naturalWidth;
+  canvas1.height = imageElement.naturalHeight;
+  canvas2.width  = imageElement.naturalWidth;
+  canvas2.height = imageElement.naturalHeight;
+
+  var ctx1 = canvas1.getContext('2d');
+  var ctx2 = canvas2.getContext('2d');
+
+  ctx1.drawImage(imageElement, 0, 0);
+  ctx2.drawImage(imageBitmap, 0, 0);
+
+  document.body.appendChild(canvas1);
+  document.body.appendChild(canvas2);
+
+  for (var t = 0; t < 20; ++t) {
+    // check one random pixel
+    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
+    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
+    isPixel(ctx1, ctx2, randomX, randomY);
+  }
+}
+
+function prepareTwoImageBitmap() {
+  gImage1 = document.createElement('img');
+  gImage2 = document.createElement('img');
+  gImage1.src = "image_rgrg-256x256.png";
+  gImage2.src = "image_yellow.png";
+
+  var p1 = new Promise(function(resolve, reject) {
+    gImage1.onload = function() {
+      var promise = createImageBitmap(gImage1);
+      promise.then(function(bitmap) {
+        gImageBitmap1 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  var p2 = new Promise(function(resolve, reject) {
+    gImage2.onload = function() {
+      var promise = createImageBitmap(gImage2);
+      promise.then(function(bitmap) {
+        gImageBitmap2 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  return Promise.all([p1, p2]);
+}
+
+function runTests() {
+  window.onmessage = function(event) {
+    if (event.data.type == "status") {
+      ok(event.data.status, event.data.msg);
+    } else if (event.data.type == "finish") {
+      SimpleTest.finish();
+    } else if (event.data.type == "bitmap1") {
+      compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
+    } else if (event.data.type == "bitmap2") {
+      compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
+    }
+  }
+
+  var div = document.getElementById("content");
+  ok(div, "Parent exists");
+
+  var ifr = document.createElement("iframe");
+  ifr.addEventListener("load", iframeLoaded, false);
+  ifr.setAttribute('src', "imagebitmap_structuredclone_iframe.html");
+  div.appendChild(ifr);
+
+  function iframeLoaded() {
+    prepareTwoImageBitmap().then(function(){
+      ifr.contentWindow.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2}, "*");
+    });
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_structuredclone_window.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap : StructuredClone main window to main window</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<script type="text/javascript">
+
+var gImage1;
+var gImage2;
+var gImageBitmap1;
+var gImageBitmap2;
+
+function isPixel(ctx1, ctx2, x, y) {
+  var pixel1 = ctx1.getImageData(x, y, 1, 1);
+  var pixel2 = ctx2.getImageData(x, y, 1, 1);
+  ok(pixel1.data[0] == pixel2.data[0] &&
+     pixel1.data[1] == pixel2.data[1] &&
+     pixel1.data[2] == pixel2.data[2] &&
+     pixel1.data[3] == pixel2.data[3],
+    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
+}
+
+function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+
+  canvas1.width  = imageElement.naturalWidth;
+  canvas1.height = imageElement.naturalHeight;
+  canvas2.width  = imageElement.naturalWidth;
+  canvas2.height = imageElement.naturalHeight;
+
+  var ctx1 = canvas1.getContext('2d');
+  var ctx2 = canvas2.getContext('2d');
+
+  ctx1.drawImage(imageElement, 0, 0);
+  ctx2.drawImage(imageBitmap, 0, 0);
+
+  document.body.appendChild(canvas1);
+  document.body.appendChild(canvas2);
+
+  for (var t = 0; t < 20; ++t) {
+    // check one random pixel
+    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
+    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
+    isPixel(ctx1, ctx2, randomX, randomY);
+  }
+}
+
+window.onmessage = function(event) {
+  compareImageBitmapWithImageElement(event.data.bitmap1, gImage1);
+  compareImageBitmapWithImageElement(event.data.bitmap2, gImage2);
+  SimpleTest.finish();
+}
+
+function prepareTwoImageBitmap() {
+  gImage1 = document.createElement('img');
+  gImage2 = document.createElement('img');
+  gImage1.src = "image_rgrg-256x256.png";
+  gImage2.src = "image_yellow.png";
+
+  var p1 = new Promise(function(resolve, reject) {
+    gImage1.onload = function() {
+      var promise = createImageBitmap(gImage1);
+      promise.then(function(bitmap) {
+        gImageBitmap1 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  var p2 = new Promise(function(resolve, reject) {
+    gImage2.onload = function() {
+      var promise = createImageBitmap(gImage2);
+      promise.then(function(bitmap) {
+        gImageBitmap2 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  return Promise.all([p1, p2]);
+}
+
+function runTests() {
+  prepareTwoImageBitmap().then(function(){
+    window.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2}, "*");
+  });
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+</script>
+</body>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -671,16 +671,18 @@ var interfaceNamesInGlobalScope =
     "IDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBTransaction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBVersionChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Image",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmap",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ImageCapture", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ImageCaptureErrorEvent", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "InputEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -145,16 +145,18 @@ var interfaceNamesInGlobalScope =
     "IDBOpenDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBTransaction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBVersionChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmap",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageChannel",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessagePort",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -139,16 +139,18 @@ var interfaceNamesInGlobalScope =
     "IDBOpenDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBTransaction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBVersionChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmap",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageChannel",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessagePort",
 // IMPORTANT: Do not change this list without review from a DOM peer!