<!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/canvas/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 src="imagebitmap_bug1239300.js"></script>
<script src="imagebitmap_bug1239752.js"></script>
<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, "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/canvas/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(imageBitmap => {
      ok(!!imageBitmap, "ImageBitmaps are always created");
      const context = document.createElement("canvas").getContext("2d");
      context.drawImage(imageBitmap, 0, 0);
      try {
        context.getImageData(0, 0, 1, 1);
        ok(false, "Did not get SecurityError with unclean source. ImageBitmap was created successfully.");
      } catch (ex) {
        if (ex == "SecurityError: The operation is insecure.") {
          ok(true, ex.message);
        }
        else {
          ok(false, "Did not get SecurityError with unclean source. Error Message: " + ex.message);
        }
      }
    });
  }

  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 patternCanvas = document.createElement('canvas');
  patternCanvas.width = "512";
  patternCanvas.height = "512";
  var patternCtx = patternCanvas.getContext('2d');

  var image = new Image();
  image.src = 'image_rgrg-256x256.png';
  image.onload = function() {
    var p = createImageBitmap(image);
    p.then(function(bitmap) {
      patternCtx.rect(0, 0, 512, 512);
      patternCtx.fillStyle = patternCtx.createPattern(bitmap, "repeat");
      patternCtx.fill();
      document.body.appendChild(patternCanvas);
    });
    resolve(p);
  }

  return promise.then(function() {
    TEST_PATTERN.forEach(function(test) {
      isPixel(patternCtx, 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(testBug1239300)
    .then(testBug1239752)
    .then(SimpleTest.finish, function(ev) { failed(ev); SimpleTest.finish(); });
}

addLoadEvent(runTests);

</script>
</body>
