<!DOCTYPE html><!-- webkit-test-runner [ CSSPaintingAPIEnabled=true ] -->
<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
<meta name="assert" content="registerPaint accesses arguments in the correct order">
<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
<script src="resources/testharness.js"></script>

<style>
  #paint {
    background-image: paint(my-paint);
    width: 150px;
    height: 150px;
  }
</style>

<div id="paint"></div>

<script id="code" type="text/worklet">
class MyPaint {
  paint() { }
}

test(function() {
  registerPaint('basic1', class {
    static get inputProperties() { return ["--test"]; }
    paint() { }
  });
}, 'test that registerPaint runs');

test(function() {
  globalThis.registerPaint('basic1-globalThis', class {
    static get inputProperties() { return ["--test"]; }
    paint() { }
  });
}, 'test that registerPaint runs (called on globalThis)');

test(function() {
  registerPaint('basic2', class {
    paint() { }
  });
}, 'test that registerPaint runs without inputProperties');

test(function() {
  registerPaint('basic3', class {
    static get inputArguments() { return ["<length>"]; }
    paint() { }
  });
}, 'test that registerPaint runs with inputArguments');

test(function() {
  registerPaint('basic4', class {
    static get contextOptions() { return { alpha: true }; }
    paint() { }
  });
}, 'test that registerPaint runs with contextOptions');

test(function() {
  registerPaint('basic5', MyPaint);
  assert_throws({'name': 'InvalidModificationError'}, () => registerPaint('basic5', MyPaint));
  registerPaint('basic6', class extends MyPaint { });
}, 'test that registerPaint runs with predefined class');

test(function() {
  assert_throws({'name': 'TypeError'}, () => registerPaint('basic6', 'test'));
  assert_throws(new TypeError(), () => registerPaint('basic8'));
  assert_throws(new TypeError(), () => registerPaint('basic9', []));
  assert_throws(new TypeError(), () => registerPaint('basic10', {}));
  assert_throws(new TypeError(), () => registerPaint('basic11', 5));
  assert_throws(new TypeError(), () => registerPaint('', 5));
}, 'test that registerPaint accepts only a string and a class constructor');

test(function () {
    const calls = [];
    const proxy = new Proxy(class extends MyPaint { }, {
        get: function (target, name) {
            calls.push(name);
            return target[name];
        }
    });
    registerPaint('test-proto', proxy);
    assert_array_equals(calls, ["inputProperties", "inputArguments", "contextOptions", "prototype"]);
}, 'must get "prototype" property of the constructor');

test(function () {
    const calls = [];
    const proxy = new Proxy(class extends MyPaint { }, {
        get: function (target, name) {
            calls.push(name);
            if (name == 'inputProperties')
                throw {name: 'expectedError'};
            return target[name];
        }
    });
    assert_throws({'name': 'expectedError'}, function () { registerPaint('test-rethrow-inputProperties0', proxy); });
}, 'must rethrow an exception thrown while getting "inputProperties" property of the constructor');

test(function () {
    const calls = [];
    const proxy = new Proxy(class extends MyPaint { }, {
        get: function (target, name) {
            calls.push(name);
            if (name == 'prototype')
                throw {name: 'expectedError'};
            return target[name];
        }
    });
    assert_throws({'name': 'expectedError'}, function () { registerPaint('test-rethrow-proto', proxy); });
}, 'must rethrow an exception thrown while getting "prototype" property of the constructor');

test(function () {
    let returnedValue;
    const proxy = new Proxy(class extends MyPaint { }, {
        get: function (target, name) {
          if (name == 'prototype')
            return returnedValue;
          return target[name];
        }
    });

    returnedValue = null;
    assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj1', proxy); },
        'must throw when "prototype" property of the constructor is null');
    returnedValue = undefined;
    assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj2', proxy); },
        'must throw when "prototype" property of the constructor is undefined');
    returnedValue = 'hello';
    assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj3', proxy);; },
        'must throw when "prototype" property of the constructor is a string');
    returnedValue = 1;
    assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj4', proxy);; },
        'must throw when "prototype" property of the constructor is a number');

}, 'must throw when "prototype" property of the constructor is not an object');

test(function () {
    const constructor = function () {}
    constructor.prototype.paint = function() {}
    const calls = [];
    constructor.prototype = new Proxy(constructor.prototype, {
        get: function (target, name) {
            calls.push(name);
            return target[name];
        }
    });
    registerPaint('callbacks', constructor);
    assert_array_equals(calls, ['paint']);
}, 'must get paint callback of the constructor prototype');

test(function () {
    const constructor = function () {}
    const calls = [];
    constructor.prototype = new Proxy(constructor.prototype, {
        get: function (target, name) {
            calls.push(name);
            if (name == 'paint')
                throw {name: 'expectedError'};
            return target[name];
        }
    });
    assert_throws({'name': 'expectedError'}, function () { registerPaint('callbacks-throw', constructor); });
    assert_array_equals(calls, ['paint']);
}, 'must rethrow an exception thrown while getting paint callback on the constructor prototype');

test(function () {
    const constructor = function () {}
    const calls = [];
    constructor.prototype = new Proxy(constructor.prototype, {
        get: function (target, name) {
            calls.push(name);
            if (name == 'paint')
                return 1;
            return target[name];
        }
    });
    assert_throws({'name': 'TypeError'}, function () { registerPaint('callbacks-throw2', constructor); });
    assert_array_equals(calls, ['paint']);
}, 'must rethrow an exception thrown while converting paint callback value to Function callback type');

test(function () {
  const calls = [];
  const proxy = new Proxy(class extends MyPaint { }, {
      get: function (target, name) {
          calls.push(name);
          if (name == 'inputArguments')
            return 1;
          return target[name];
      }
  });
  assert_throws({'name': 'TypeError'}, function () { registerPaint('sequence-throw', proxy); });
  assert_array_equals(calls, ["inputProperties", "inputArguments"]);
}, 'must rethrow an exception thrown while converting the value of inputArguments to sequence<DOMString>');

test(function () {
    const constructor = function () {}
    constructor.inputArguments = {[Symbol.iterator]: function *() {
        yield '<length>';
        throw {name: 'SomeError'};
    }};
    assert_throws({'name': 'SomeError'}, function () { registerPaint('sequence-throw2', constructor); });
}, 'must rethrow an exception thrown while iterating over inputArguments to sequence<DOMString>');

test(function () {
    const constructor = function () {}
    constructor.inputArguments = {[Symbol.iterator]: 1};
    assert_throws({'name': 'TypeError'}, function () { registerPaint('symbol-iterator-throw2', constructor); });
}, 'must rethrow an exception thrown while retrieving Symbol.iterator on inputArguments');

assert_true(PaintWorkletGlobalScope.hasRunATest);
if (PaintWorkletGlobalScope.errors) {
  console.log("The following (" + PaintWorkletGlobalScope.errors.length + ") tests failed: ")
  console.log(JSON.stringify(PaintWorkletGlobalScope.errors));
  assert_true(false);
}

class MyPaint2 {
  paint(ctx, geom, properties) {
    for (var i = 0; i < 6; i++){
      for (var j = 0; j < 6; j++){
        ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' +
                         Math.floor(255 - 42.5 * j) + ',0)';
        ctx.fillRect(j * 25, i * 25, 25, 25);
      }
    }
  }
}
registerPaint('my-paint', MyPaint2);
</script>

<script>
  importWorklet(CSS.paintWorklet, document.getElementById('code').textContent);
</script>
