summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/shape-detection
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/shape-detection
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/shape-detection')
-rw-r--r--testing/web-platform/tests/shape-detection/META.yml3
-rw-r--r--testing/web-platform/tests/shape-detection/README.md63
-rw-r--r--testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html56
-rw-r--r--testing/web-platform/tests/shape-detection/detected-postMessage.https.html90
-rw-r--r--testing/web-platform/tests/shape-detection/detection-Blob.https.window.js19
-rw-r--r--testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html125
-rw-r--r--testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html33
-rw-r--r--testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html34
-rw-r--r--testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html86
-rw-r--r--testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html67
-rw-r--r--testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js44
-rw-r--r--testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html66
-rw-r--r--testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html46
-rw-r--r--testing/web-platform/tests/shape-detection/detection-ImageData.https.html68
-rw-r--r--testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js41
-rw-r--r--testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js19
-rw-r--r--testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js24
-rw-r--r--testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html25
-rw-r--r--testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js54
-rw-r--r--testing/web-platform/tests/shape-detection/detection-options.https.html54
-rw-r--r--testing/web-platform/tests/shape-detection/detection-security-test.https.html82
-rw-r--r--testing/web-platform/tests/shape-detection/detector-same-object.https.html67
-rw-r--r--testing/web-platform/tests/shape-detection/idlharness.https.any.js19
-rw-r--r--testing/web-platform/tests/shape-detection/resources/aztec-correction.jpgbin0 -> 47787 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/aztec-full.jpgbin0 -> 28632 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/aztec-layers.jpgbin0 -> 95666 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/aztec.jpgbin0 -> 23060 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/barcodes.movbin0 -> 1200741 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/code128-height.jpgbin0 -> 55292 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/code128.jpgbin0 -> 22948 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpgbin0 -> 185080 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpgbin0 -> 191912 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/face-center.jpgbin0 -> 177637 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/face-top-left.jpgbin0 -> 168676 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/face-top-right.jpgbin0 -> 171771 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/faces.movbin0 -> 2793460 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpgbin0 -> 32027 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpgbin0 -> 25472 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpgbin0 -> 40094 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpgbin0 -> 49828 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpgbin0 -> 30318 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-square.jpgbin0 -> 51406 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpgbin0 -> 43642 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpgbin0 -> 30318 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/pdf417.jpgbin0 -> 38048 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpgbin0 -> 34846 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpgbin0 -> 34846 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-center.jpgbin0 -> 34838 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-h.jpgbin0 -> 54287 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-l.jpgbin0 -> 34210 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-m.jpgbin0 -> 34838 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-q.jpgbin0 -> 43431 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-top-left.jpgbin0 -> 34968 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/qr-top-right.jpgbin0 -> 34968 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js75
-rw-r--r--testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js66
-rw-r--r--testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpgbin0 -> 25605 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpgbin0 -> 25982 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/text-center.jpgbin0 -> 25646 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/text-top-left.jpgbin0 -> 26305 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/text-top-right.jpgbin0 -> 26364 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/resources/text.movbin0 -> 132839 bytes
-rw-r--r--testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html66
-rw-r--r--testing/web-platform/tests/shape-detection/single-barcode-detection.https.html396
-rw-r--r--testing/web-platform/tests/shape-detection/single-face-detection.https.html223
-rw-r--r--testing/web-platform/tests/shape-detection/single-text-detection.https.html197
66 files changed, 2208 insertions, 0 deletions
diff --git a/testing/web-platform/tests/shape-detection/META.yml b/testing/web-platform/tests/shape-detection/META.yml
new file mode 100644
index 0000000000..7754624047
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/META.yml
@@ -0,0 +1,3 @@
+spec: https://wicg.github.io/shape-detection-api/
+suggested_reviewers:
+ - yellowdoge
diff --git a/testing/web-platform/tests/shape-detection/README.md b/testing/web-platform/tests/shape-detection/README.md
new file mode 100644
index 0000000000..624b021336
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/README.md
@@ -0,0 +1,63 @@
+The `shapedetection-helpers.js` tests require implementations of
+the `FaceDetectionTest`, `BarcodeDetectionTest` and `TextDetectionTest`
+interfaces, which should emulate platform shape detection backends.
+
+The `FaceDetectionTest` interface is defined as:
+
+```
+ class FaceDetectionTest {
+ async initialize(); // Sets up the testing environment.
+ async reset(); // Frees the resources.
+ MockFaceDetectionProvider(); //Returns `MockFaceDetectionProvider` interface.
+ };
+
+ class MockFaceDetectionProvider {
+ getFrameData(); //Gets frame data of detection result.
+ getMaxDetectedFaces(); //Gets value of `maxDetectedFaces` in `FaceDetector` constructor
+ getFastMode(); //Gets value of `fastMode` in `FaceDetector` constructor
+ };
+```
+
+The Chromium implementation of the `FaceDetectionTest` interface is located in
+[mock-facedetection.js](../resources/chromium/mock-facedetection.js).
+
+The `BarcodeDetectionTest` interface is defined as:
+
+```
+ class BarcodeDetectionTest {
+ async initialize(); // Sets up the testing environment.
+ async reset(); // Frees the resources.
+ MockBarcodeDetectionProvider(); //Returns `MockBarcodeDetectionProvider` interface.
+ };
+
+ class MockBarcodeDetectionProvider {
+ async enumerateSupportedFormats(); //Enumerates supported formats
+ getFrameData(); //Gets frame data of detection result.
+ getFormats(); //Gets value of `formats` in `BarcodeDetector` constructor
+ simulateNoImplementation(); // simulate a 'no implementation available' case
+ };
+```
+
+The Chromium implementation of the `BarcodeDetectionTest` interface is located in
+[mock-barcodedetection.js](../resources/chromium/mock-barcodedetection.js).
+
+The `TextDetectionTest` interface is defined as:
+
+```
+ class TextDetectionTest {
+ async initialize(); // Sets up the testing environment.
+ async reset(); // Frees the resources.
+ MockTextDetection(); //Returns `MockTextDetection` interface.
+ };
+
+ class MockTextDetection {
+ getFrameData(); //Gets frame data of detection result.
+ };
+```
+
+The Chromium implementation of the `TextDetectionTest` interface is located in
+[mock-textdetection.js](../resources/chromium/mock-textdetection.js).
+
+Other browser vendors should provide their own implementations of
+the `FaceDetectionTest`, `BarcodeDetectionTest` and `TextDetectionTest`
+interfaces.
diff --git a/testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html b/testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html
new file mode 100644
index 0000000000..dcf379b97a
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that detected{Face, Barcode, Text}'s boundingBox
+// should be DOMRectReadOnly.
+const imageDataTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ name: "Face - detectedFace.boundingBox should be DOMRectReadOnly"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ name: "Barcode - detectedBarcode.boundingBox should be DOMRectReadOnly"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ name: "Text - detectedText.boundingBox should be DOMRectReadOnly"
+ }
+ ];
+
+for (let imageDataTest of imageDataTests) {
+ detection_test(imageDataTest.mockTestName, async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/green-16x16.png";
+ await imgWatcher.wait_for("load");
+
+ const canvas = document.createElement("canvas");
+ canvas.getContext("2d").drawImage(img, 0, 0);
+
+ const detector = imageDataTest.createDetector();
+ const detectionResult = await detector.detect(canvas.getContext("2d")
+ .getImageData(0, 0, canvas.width, canvas.height));
+ CheckDetectedReadOnlyBoundingBoxes(detectionResult);
+ }, imageDataTest.name);
+}
+
+function CheckDetectedReadOnlyBoundingBoxes(detectedObjects) {
+ const properties =
+ ['x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left'];
+
+ detectedObjects.forEach(detectedObject => {
+ properties.forEach(property => {
+ assert_readonly(detectedObject.boundingBox, property);
+ });
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detected-postMessage.https.html b/testing/web-platform/tests/shape-detection/detected-postMessage.https.html
new file mode 100644
index 0000000000..8066984b26
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detected-postMessage.https.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that Detected{Face, Barcode, Text} can be passed to
+// postMessage().
+const postMessageTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - DetectedFace can be passed to postMessage()"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - DetectedBarcode can be passed to postMessage()"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - DetectedText can be passed to postMessage()",
+ },
+ ];
+
+for (let postMessageTest of postMessageTests) {
+ detection_test(postMessageTest.mockTestName, async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/green-16x16.png";
+ await imgWatcher.wait_for("load");
+
+ const canvas = document.createElement("canvas");
+ canvas.getContext("2d").drawImage(img, 0, 0);
+
+ const detector = postMessageTest.createDetector();
+ const detectionResult = await detector.detect(canvas.getContext("2d")
+ .getImageData(0, 0, canvas.width, canvas.height));
+
+ const msgWatcher = new EventWatcher(t, window, ['message']);
+ window.postMessage(detectionResult);
+ const evt = await msgWatcher.wait_for('message');
+ postMessageTest.detectionResultTest(evt.data)
+ }, postMessageTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult) {
+ assert_equals(detectionResult.length, 3, "Number of faces");
+ assert_equals(detectionResult[0].landmarks.length, 2, "Number of landmarks");
+ assert_object_equals(detectionResult[0].landmarks[0],
+ {type : 'eye', locations : [{x : 4.0, y : 5.0}]},
+ "landmark #1");
+ assert_equals(detectionResult[0].landmarks[1].locations.length, 8,
+ "Number of locations along eye");
+ assert_object_equals(detectionResult[1].landmarks[0],
+ {type : 'nose', locations : [{x : 100.0, y : 50.0}]},
+ "landmark #2");
+ assert_equals(detectionResult[1].landmarks[1].locations.length, 9,
+ "Number of locations along nose");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult) {
+ assert_equals(detectionResult.length, 2, "Number of barcodes");
+ assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+ assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+ assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+ assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+function TextDetectorDetectionResultTest(detectionResult) {
+ assert_equals(detectionResult.length, 2, "Number of textBlocks");
+ assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
+ assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2");
+ for (let i = 0; i < detectionResult.length; i++) {
+ assert_equals(detectionResult[i].boundingBox.x, detectionResult[i].cornerPoints[0].x);
+ assert_equals(detectionResult[i].boundingBox.y, detectionResult[i].cornerPoints[0].y);
+ assert_equals(detectionResult[i].boundingBox.width,
+ detectionResult[i].cornerPoints[2].x - detectionResult[i].cornerPoints[3].x);
+ assert_equals(detectionResult[i].boundingBox.height,
+ detectionResult[i].cornerPoints[2].y - detectionResult[i].cornerPoints[1].y);
+ }
+
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-Blob.https.window.js b/testing/web-platform/tests/shape-detection/detection-Blob.https.window.js
new file mode 100644
index 0000000000..401a9cc72d
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-Blob.https.window.js
@@ -0,0 +1,19 @@
+'use strict';
+
+promise_test(async (t) => {
+ const blob = new Blob(['not really a png'], {type: 'image/png'});
+ const detector = new FaceDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(blob));
+}, 'FaceDetector.detect() rejects on a Blob');
+
+promise_test(async (t) => {
+ const blob = new Blob(['not really a png'], {type: 'image/png'});
+ const detector = new BarcodeDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(blob));
+}, 'BarcodeDetector.detect() rejects on a Blob');
+
+promise_test(async (t) => {
+ const blob = new Blob(['not really a png'], {type: 'image/png'});
+ const detector = new TextDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(blob));
+}, 'TextDetector.detect() rejects on a Blob');
diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html
new file mode 100644
index 0000000000..4773bc8a66
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an HTMLCanvasElement
+// and on an OffscreenCanvas.
+const canvasElementTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ createCanvas: () => { return document.createElement("canvas"); },
+ pixelFormat: "uint8",
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(HTMLCanvasElement)"
+ },
+ {
+ createDetector: () => { return new FaceDetector(); },
+ createCanvas: () => { return document.createElement("canvas"); },
+ pixelFormat: "float16",
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(HTMLCanvasElementF16Format)"
+ },
+ {
+ createDetector: () => { return new FaceDetector(); },
+ createCanvas: () => { return new OffscreenCanvas(300, 150); },
+ pixelFormat: "uint8",
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(OffscreenCanvas)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ createCanvas: () => { return document.createElement("canvas"); },
+ pixelFormat: "uint8",
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(HTMLCanvasElement)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ createCanvas: () => { return document.createElement("canvas"); },
+ pixelFormat: "float16",
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(HTMLCanvasElementF16Format)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ createCanvas: () => { return new OffscreenCanvas(300, 150); },
+ pixelFormat: "uint8",
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(OffscreenCanvas)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ createCanvas: () => { return document.createElement("canvas"); },
+ pixelFormat: "uint8",
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(HTMLCanvasElement)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ createCanvas: () => { return document.createElement("canvas"); },
+ pixelFormat: "float16",
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(HTMLCanvasElementF16Format)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ createCanvas: () => { return new OffscreenCanvas(300, 150); },
+ pixelFormat: "uint8",
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(OffscreenCanvas)"
+ }
+ ];
+
+for (let canvasElementTest of canvasElementTests) {
+ detection_test(canvasElementTest.mockTestName, async (t, detectionTest) => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/green-16x16.png";
+ await imgWatcher.wait_for("load");
+ const canvas = canvasElementTest.createCanvas();
+ canvas.getContext(
+ "2d", { pixelFormat: canvasElementTest.pixelFormat }).drawImage(
+ img, 0, 0);
+
+ const detector = canvasElementTest.createDetector();
+ const detectionResult = await detector.detect(canvas);
+ canvasElementTest.detectionResultTest(detectionResult, detectionTest);
+ }, canvasElementTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+ const imageReceivedByMock =
+ mockTest.MockFaceDetectionProvider().getFrameData();
+ assert_equals(imageReceivedByMock.byteLength, 180000, "Image length");
+ const GREEN_PIXEL = 0xFF00FF00;
+ assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+ assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of barcodes");
+ assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+ assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+ assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+ assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+function TextDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of textBlocks");
+ assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
+ assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2");
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html
new file mode 100644
index 0000000000..40eee24494
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// This test verifies *Detector.detect() rejects the promise when fed with
+// an HTMLImageElement with an empty 'src'.
+const emptyInputTests = [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ name: "Face - detect(empty src)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ name: "Barcode - detect(empty src)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ name: "Text - detect(empty src)"
+ }
+];
+
+for (let emptyInputTest of emptyInputTests) {
+ promise_test(async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "";
+ await imgWatcher.wait_for("error");
+
+ const detector = emptyInputTest.createDetector();
+ promise_rejects_dom(t, "InvalidStateError", detector.detect(img));
+ }, emptyInputTest.name);
+}
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html
new file mode 100644
index 0000000000..621dd4e23f
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// This test verifies *Detector.detect() returns an empty list when fed with
+// an HTMLImageElement with an image with 0x0 dimensions.
+const zeroDimensionsTests = [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ name: "Face - detect(0x0)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ name: "Barcode - detect(0x0)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ name: "Text - detect(0x0)"
+ }
+];
+
+for (let zeroDimensionsTest of zeroDimensionsTests) {
+ promise_test(async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/red-zerosize.svg";
+ await imgWatcher.wait_for("load");
+
+ const detector = zeroDimensionsTest.createDetector();
+ const detectionResult = await detector.detect(img);
+ assert_equals(detectionResult.length, 0);
+ }, zeroDimensionsTest.name);
+}
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html
new file mode 100644
index 0000000000..f3b994c258
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<body>
+<img id="img" src="/images/green-16x16.png"/>
+</body>
+<script>
+
+// These tests verify that a Detector's detect() works on an HTMLImageElement.
+const imageElementTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(HTMLImageElement)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(HTMLImageElement)",
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(HTMLImageElement)"
+ }
+ ];
+
+for (let imageElementTest of imageElementTests) {
+ detection_test(imageElementTest.mockTestName, async (t, detectionTest) => {
+ const img = document.getElementById("img");
+
+ const detector = imageElementTest.createDetector();
+ const detectionResult = await detector.detect(img);
+ imageElementTest.detectionResultTest(detectionResult, detectionTest);
+ }, imageElementTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+ const imageReceivedByMock =
+ mockTest.MockFaceDetectionProvider().getFrameData();
+ assert_equals(imageReceivedByMock.byteLength, 1024, "Image length");
+ const GREEN_PIXEL = 0xFF00FF00;
+ assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+ assert_equals(detectionResult.length, 3, "Number of faces");
+ assert_equals(detectionResult[0].landmarks.length, 2, "Number of landmarks");
+ assert_object_equals(detectionResult[0].landmarks[0],
+ {type : 'eye', locations : [{x : 4.0, y : 5.0}]},
+ "landmark #1");
+ assert_equals(detectionResult[0].landmarks[1].locations.length, 8,
+ "Number of locations along eye");
+ assert_object_equals(detectionResult[1].landmarks[0],
+ {type : 'nose', locations : [{x : 100.0, y : 50.0}]},
+ "landmark #2");
+ assert_equals(detectionResult[1].landmarks[1].locations.length, 9,
+ "Number of locations along nose");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of barcodes");
+ assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+ assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+ assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+ assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+function TextDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of textBlocks");
+ assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
+ assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2");
+ for (let i = 0; i < detectionResult.length; i++) {
+ assert_equals(detectionResult[i].boundingBox.x, detectionResult[i].cornerPoints[0].x);
+ assert_equals(detectionResult[i].boundingBox.y, detectionResult[i].cornerPoints[0].y);
+ assert_equals(detectionResult[i].boundingBox.width,
+ detectionResult[i].cornerPoints[2].x - detectionResult[i].cornerPoints[3].x);
+ assert_equals(detectionResult[i].boundingBox.height,
+ detectionResult[i].cornerPoints[2].y - detectionResult[i].cornerPoints[1].y);
+ }
+
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html
new file mode 100644
index 0000000000..2ce379bafc
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an HTMLVideoElement.
+const videoElementTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(HTMLVideoElement)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(HTMLVideoElement)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(HTMLVideoElement)"
+ }
+ ];
+
+for (let videoElementTest of videoElementTests) {
+ detection_test(videoElementTest.mockTestName, async (t, detectionTest) => {
+ const video = document.createElement("video");
+ video.src = "/media/white.webm";
+ video.loop = true;
+ video.autoplay = true;
+ const videoWatcher = new EventWatcher(t, video, ["play", "error"]);
+ video.load();
+ await videoWatcher.wait_for("play");
+
+ const detector = videoElementTest.createDetector();
+ const detectionResult = await detector.detect(video);
+ videoElementTest.detectionResultTest(detectionResult, detectionTest);
+ }, videoElementTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+ const imageReceivedByMock =
+ mockTest.MockFaceDetectionProvider().getFrameData();
+ assert_equals(imageReceivedByMock.byteLength, 307200, "Image length");
+ assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of barcodes");
+ assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+ assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+ assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+ assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+function TextDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of textBlocks");
+ assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
+ assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2");
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js b/testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js
new file mode 100644
index 0000000000..80cb373f33
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js
@@ -0,0 +1,44 @@
+'use strict';
+
+async function createClosedImageBitmap(t) {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ['load', 'error']);
+ img.src = '/images/green-16x16.png';
+ await imgWatcher.wait_for('load');
+ const imageBitmap = await createImageBitmap(img);
+ imageBitmap.close();
+ return imageBitmap;
+}
+
+promise_test(async (t) => {
+ const imageBitmap = await createClosedImageBitmap(t);
+ const detector = new FaceDetector();
+ try {
+ await detector.detect(imageBitmap);
+ assert_unreached();
+ } catch (e) {
+ assert_equals(e.code, DOMException.INVALID_STATE_ERR);
+ }
+}, 'FaceDetector.detect() rejects on a closed ImageBitmap');
+
+promise_test(async (t) => {
+ const imageBitmap = await createClosedImageBitmap(t);
+ const detector = new BarcodeDetector();
+ try {
+ await detector.detect(imageBitmap);
+ assert_unreached();
+ } catch (e) {
+ assert_equals(e.code, DOMException.INVALID_STATE_ERR);
+ }
+}, 'BarcodeDetector.detect() rejects on a closed ImageBitmap');
+
+promise_test(async (t) => {
+ const imageBitmap = await createClosedImageBitmap(t);
+ const detector = new TextDetector();
+ try {
+ await detector.detect(imageBitmap);
+ assert_unreached();
+ } catch (e) {
+ assert_equals(e.code, DOMException.INVALID_STATE_ERR);
+ }
+}, 'TextDetector.detect() rejects on a closed ImageBitmap');
diff --git a/testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html b/testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html
new file mode 100644
index 0000000000..b4302c4a51
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an ImageBitmap.
+const imageBitmapTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(ImageBitmap)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(ImageBitmap)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(ImageBitmap)",
+ }
+ ];
+
+for (let imageBitmapTest of imageBitmapTests) {
+ detection_test(imageBitmapTest.mockTestName, async (t, detectionTest) => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/green-16x16.png";
+ await imgWatcher.wait_for("load");
+ const imageBitmap = await createImageBitmap(img);
+
+ const detector = imageBitmapTest.createDetector();
+ const detectionResult = await detector.detect(imageBitmap);
+ imageBitmapTest.detectionResultTest(detectionResult, detectionTest);
+ }, imageBitmapTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+ const imageReceivedByMock = mockTest.MockFaceDetectionProvider().getFrameData();
+ assert_equals(imageReceivedByMock.byteLength, 1024, "Image length");
+ const GREEN_PIXEL = 0xFF00FF00;
+ assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+ assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of barcodes");
+ assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+ assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+ assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+ assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+function TextDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of textBlocks");
+ assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
+ assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2");
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html b/testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html
new file mode 100644
index 0000000000..b4e31c8656
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+function detachBuffer(buffer) {
+ window.postMessage('', '*', [buffer]);
+}
+
+promise_test(async () => {
+ let data = new ImageData(1024, 1024);
+ detachBuffer(data.data.buffer);
+ let detector = new FaceDetector();
+ try {
+ await detector.detect(data);
+ assert_unreached();
+ } catch (e) {
+ assert_equals(e.code, DOMException.INVALID_STATE_ERR);
+ }
+}, 'FaceDetector.detect() rejects on a detached buffer');
+
+promise_test(async () => {
+ let data = new ImageData(1024, 1024);
+ detachBuffer(data.data.buffer);
+ let detector = new BarcodeDetector();
+ try {
+ await detector.detect(data);
+ assert_unreached();
+ } catch (e) {
+ assert_equals(e.code, DOMException.INVALID_STATE_ERR);
+ }
+}, 'BarcodeDetector.detect() rejects on a detached buffer');
+
+promise_test(async () => {
+ let data = new ImageData(1024, 1024);
+ detachBuffer(data.data.buffer);
+ let detector = new TextDetector();
+ try {
+ await detector.detect(data);
+ assert_unreached();
+ } catch (e) {
+ assert_equals(e.code, DOMException.INVALID_STATE_ERR);
+ }
+}, 'TextDetector.detect() rejects on a detached buffer');
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-ImageData.https.html b/testing/web-platform/tests/shape-detection/detection-ImageData.https.html
new file mode 100644
index 0000000000..330239fdf0
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-ImageData.https.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an ImageBitmap.
+const imageDataTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: FaceDetectorDetectionResultTest,
+ name: "Face - detect(ImageData)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: BarcodeDetectorDetectionResultTest,
+ name: "Barcode - detect(ImageData)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: TextDetectorDetectionResultTest,
+ name: "Text - detect(ImageData)",
+ }
+ ];
+
+for (let imageDataTest of imageDataTests) {
+ detection_test(imageDataTest.mockTestName, async (t, detectionTest) => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/green-16x16.png";
+ await imgWatcher.wait_for("load");
+ const canvas = document.createElement("canvas");
+ canvas.getContext("2d").drawImage(img, 0, 0);
+
+ const detector = imageDataTest.createDetector();
+ const detectionResult = await detector.detect(canvas.getContext("2d")
+ .getImageData(0, 0, canvas.width, canvas.height));
+ imageDataTest.detectionResultTest(detectionResult, detectionTest);
+ }, imageDataTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+ const imageReceivedByMock = mockTest.MockFaceDetectionProvider().getFrameData();
+ assert_equals(imageReceivedByMock.byteLength, 180000,"Image length");
+ const GREEN_PIXEL = 0xFF00FF00;
+ assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+ assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of barcodes");
+ assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+ assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+ assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+ assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+function TextDetectorDetectionResultTest(detectionResult, mockTest) {
+ assert_equals(detectionResult.length, 2, "Number of textBlocks");
+ assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
+ assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2");
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js b/testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js
new file mode 100644
index 0000000000..24dd3e597c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js
@@ -0,0 +1,41 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/shape-detection/resources/shapedetection-helpers.js
+
+const imgUint16 = new ImageData(1024, 1024, {storageFormat: 'uint16'});
+
+// These tests verify that a Detector's detect() can process ImageData with
+// uint16 storage format.
+const imageDataTests = [
+ {
+ createDetector: () => {
+ return new FaceDetector();
+ },
+ mockTestName: 'FaceDetectionTest',
+ name:
+ 'FaceDetector.detect() can process uint16 storage format ImageData'
+ },
+ {
+ createDetector: () => {
+ return new BarcodeDetector();
+ },
+ mockTestName: 'BarcodeDetectionTest',
+ name:
+ 'BarcodeDetector.detect() can process uint16 storage format ImageData'
+ },
+ {
+ createDetector: () => {
+ return new TextDetector();
+ },
+ mockTestName: 'TextDetectionTest',
+ name:
+ 'TextDetector.detect() can process uint16 storage format ImageData'
+ }
+];
+
+for (let imageDataTest of imageDataTests) {
+ detection_test(imageDataTest.mockTestName, async () => {
+ let detector = imageDataTest.createDetector();
+ await detector.detect(imgUint16);
+ }, imageDataTest.name);
+}
diff --git a/testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js b/testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js
new file mode 100644
index 0000000000..22f1629ecc
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js
@@ -0,0 +1,19 @@
+'use strict';
+
+promise_test(async (t) => {
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ const detector = new FaceDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(image));
+}, 'FaceDetector.detect() rejects on an SVGImageElement');
+
+promise_test(async (t) => {
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ const detector = new BarcodeDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(image));
+}, 'BarcodeDetector.detect() rejects on an SVGImageElement');
+
+promise_test(async (t) => {
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ const detector = new TextDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(image));
+}, 'TextDetector.detect() rejects on an SVGImageElement');
diff --git a/testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js b/testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js
new file mode 100644
index 0000000000..601c60bcaf
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js
@@ -0,0 +1,24 @@
+'use strict';
+
+function createVideoFrame() {
+ const canvas = document.createElement('canvas');
+ return new VideoFrame(canvas, {timestamp: 0});
+}
+
+promise_test(async (t) => {
+ const frame = createVideoFrame();
+ const detector = new FaceDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(frame));
+}, 'FaceDetector.detect() rejects on a VideoFrame');
+
+promise_test(async (t) => {
+ const frame = createVideoFrame();
+ const detector = new BarcodeDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(frame));
+}, 'BarcodeDetector.detect() rejects on a VideoFrame');
+
+promise_test(async (t) => {
+ const frame = createVideoFrame();
+ const detector = new TextDetector();
+ await promise_rejects_dom(t, 'NotSupportedError', detector.detect(frame));
+}, 'TextDetector.detect() rejects on a VideoFrame');
diff --git a/testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html b/testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html
new file mode 100644
index 0000000000..0b4b223369
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<link rel="help" href="https://wicg.github.io/shape-detection-api/#dom-barcodedetector-getsupportedformats">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+detection_test('BarcodeDetectionTest', async t => {
+ const result = await BarcodeDetector.getSupportedFormats();
+ assert_equals(result.length, 3, 'Number of supported formats');
+ assert_equals(result[0], 'aztec', 'format 1');
+ assert_equals(result[1], 'data_matrix', 'format 2');
+ assert_equals(result[2], 'qr_code', 'format 3');
+}, 'get supported barcode formats');
+
+detection_test('BarcodeDetectionTest', async (t, detectionTest) => {
+ // Disable built-in support for barcode detection to test fallback handling.
+ detectionTest.MockBarcodeDetectionProvider().simulateNoImplementation();
+
+ const result = await BarcodeDetector.getSupportedFormats();
+ assert_equals(result.length, 0, 'result.length');
+
+}, 'getSupportedFormats() resolves with empty list when unsupported');
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js b/testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js
new file mode 100644
index 0000000000..3981c6fdc8
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js
@@ -0,0 +1,54 @@
+importScripts("/resources/testharness.js");
+importScripts("/resources/test-only-api.js");
+importScripts("resources/shapedetection-helpers.js");
+
+'use strict';
+
+// These tests verify that a Detector's detect() works on an
+// ImageBitmap on workers.
+const imageBitmapTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ resultSize: 3, // Number of faces
+ detectorType: "Face"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ resultSize: 2, // Number of barcodes
+ detectorType: "Barcode"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ resultSize: 2, // Number of text blocks
+ detectorType: "Text"
+ }
+ ];
+
+for (let imageBitmapTest of imageBitmapTests) {
+ // ImageBitmap is of transferable type and can be sent to and
+ // tested on worker.
+ detection_test(imageBitmapTest.mockTestName, async (t, detectionTest) => {
+ const img = createTestImage();
+ const theImageBitmap = await createImageBitmap(img);
+ const detector = imageBitmapTest.createDetector();
+ const detectionResult = await detector.detect(theImageBitmap);
+ assert_equals(detectionResult.length, imageBitmapTest.resultSize,
+ `Number of ${imageBitmapTest.detectorType}`);
+ }, `${imageBitmapTest.detectorType} Detector detect(ImageBitmap) on worker`);
+}
+
+function createTestImage() {
+ const image = new OffscreenCanvas(100, 50);
+ const imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+done();
diff --git a/testing/web-platform/tests/shape-detection/detection-options.https.html b/testing/web-platform/tests/shape-detection/detection-options.https.html
new file mode 100644
index 0000000000..4b79da2a6e
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-options.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<body>
+<img id="img" src="/images/green-16x16.png"/>
+</body>
+<script>
+
+detection_test("FaceDetectionTest", async (t, detectionTest) => {
+ const img = document.getElementById("img");
+ const mock = detectionTest.MockFaceDetectionProvider();
+
+ const detectorWithDefault = new FaceDetector();
+ let faceDetectionResult = await detectorWithDefault.detect(img);
+ assert_equals(mock.getMaxDetectedFaces(), 10, "default maxDetectedFaces");
+ assert_equals(mock.getFastMode(), false, "default maxDetectedFaces");
+
+ const detectorWithOptions =
+ new FaceDetector({maxDetectedFaces: 7, fastMode: true});
+ faceDetectionResult = await detectorWithOptions.detect(img);
+ assert_equals(mock.getMaxDetectedFaces(), 7, "maxDetectedFaces");
+ assert_equals(mock.getFastMode(), true, "maxDetectedFaces");
+}, "Test that FaceDetectionOptions are correctly propagated");
+
+detection_test("BarcodeDetectionTest", async (t, detectionTest) => {
+ const img = document.getElementById("img");
+ const mock = detectionTest.MockBarcodeDetectionProvider();
+
+ const detectorWithNoOptions = new BarcodeDetector();
+ let barcodeDetectionResult = await detectorWithNoOptions.detect(img);
+ assert_array_equals(mock.getFormats(), [], "formats");
+
+ const detectorWithOptions = new BarcodeDetector({
+ formats: ["code_128", "qr_code"]});
+ barcodeDetectionResult = await detectorWithOptions.detect(img);
+ assert_array_equals(
+ mock.getFormats(),
+ [BarcodeFormat.CODE_128, BarcodeFormat.QR_CODE],
+ "formats");
+
+ const invalidFormats = [
+ [],
+ ["unknown"],
+ ["foo", "bar"]
+ ];
+
+ invalidFormats.forEach(invalidFormat => {
+ assert_throws_js(TypeError, () => new BarcodeDetector({formats: invalidFormat}));
+ });
+
+}, "Test that BarcodeDetectorOptions are correctly propagated");
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detection-security-test.https.html b/testing/web-platform/tests/shape-detection/detection-security-test.https.html
new file mode 100644
index 0000000000..4d87238dad
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detection-security-test.https.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// Detectors should reject undecodable images with an InvalidStateError.
+const badImageTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ name: "Face - detect(broken image)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ name: "Barcode - detect(broken image)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ name: "Text - detect(broken image)"
+ }
+ ];
+
+for (let badImageTest of badImageTests) {
+ // This test verifies that a Detector will reject an undecodable image.
+ promise_test(async t => {
+ const img = new Image();
+ const error =
+ await detectOnElementAndExpectError(badImageTest.createDetector,
+ img, "/images/broken.png");
+ assert_equals(error.name, "InvalidStateError");
+ }, badImageTest.name);
+}
+
+// Detectors should reject undecodable videos with an InvalidStateError.
+const badVideoTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ name: "Face - detect(broken video)"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ name: "Barcode - detect(broken video)"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ name: "Text - detect(broken video)"
+ }
+ ];
+
+for (let badVideoTest of badVideoTests) {
+ // This test verifies that a Detector will reject a broken video.
+ promise_test(async t => {
+ const video = document.createElement('video');
+ const error =
+ await detectOnElementAndExpectError(badVideoTest.createDetector,
+ video, "garbage.webm");
+ assert_equals(error.name, "InvalidStateError");
+ }, badVideoTest.name);
+}
+
+// Returns a Promise that is resolve()d if detect() is rejected. Needs an input
+// |element| (e.g. an HTMLImageElement or HTMLVideoElement) and a |url| to load.
+function detectOnElementAndExpectError(createDetector, element, url) {
+ return new Promise((resolve, reject) => {
+ const tryDetection = async () => {
+ const detector = createDetector();
+ try {
+ const detectionResult = await detector.detect(element);
+ reject("Promise should have been rejected.");
+ } catch (error) {
+ resolve(error);
+ }
+ };
+ element.onload = tryDetection;
+ element.onerror = tryDetection;
+ element.src = url;
+ });
+};
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/detector-same-object.https.html b/testing/web-platform/tests/shape-detection/detector-same-object.https.html
new file mode 100644
index 0000000000..bf7c068041
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/detector-same-object.https.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that detect()ed Detected{Barcode,Face, Text}'s individual
+// fields are [SameObject].
+const imageDataTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ mockTestName: "FaceDetectionTest",
+ detectionResultTest: CheckDetectedFaceSameObjects,
+ name: "Face - detect(ImageData), [SameObject]"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ mockTestName: "BarcodeDetectionTest",
+ detectionResultTest: CheckDetectedBarcodesSameObjects,
+ name: "Barcode - detect(ImageData), [SameObject]"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ mockTestName: "TextDetectionTest",
+ detectionResultTest: CheckDetectedTextBlocksSameObjects,
+ name: "Text - detect(ImageData), [SameObject]",
+ }
+ ];
+
+for (let imageDataTest of imageDataTests) {
+ detection_test(imageDataTest.mockTestName, async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = "/images/green-16x16.png";
+ await imgWatcher.wait_for("load");
+ const canvas = document.createElement("canvas");
+ canvas.getContext("2d").drawImage(img, 0, 0);
+
+ const detector = imageDataTest.createDetector();
+ const detectionResult = await detector.detect(canvas.getContext("2d")
+ .getImageData(0, 0, canvas.width, canvas.height));
+ imageDataTest.detectionResultTest(detectionResult);
+ }, imageDataTest.name);
+}
+
+function CheckDetectedFaceSameObjects(detectedFaces) {
+ assert_greater_than(detectedFaces.length, 0, "Number of faces");
+ assert_equals(detectedFaces[0].boundingBox, detectedFaces[0].boundingBox);
+ assert_equals(detectedFaces[0].landmarks, detectedFaces[0].landmarks);
+}
+
+function CheckDetectedBarcodesSameObjects(detectedBarcodes) {
+ assert_greater_than(detectedBarcodes.length, 0, "Number of barcodes");
+ assert_equals(detectedBarcodes[0].rawValue, detectedBarcodes[0].rawValue);
+ assert_equals(detectedBarcodes[0].boundingBox, detectedBarcodes[0].boundingBox);
+ assert_equals(detectedBarcodes[0].format, detectedBarcodes[0].format);
+ assert_equals(detectedBarcodes[0].cornerPoints, detectedBarcodes[0].cornerPoints);
+}
+
+function CheckDetectedTextBlocksSameObjects(detectedTextBlocks) {
+ assert_greater_than(detectedTextBlocks.length, 0, "Number of textBlocks");
+ assert_equals(detectedTextBlocks[0].rawValue, detectedTextBlocks[0].rawValue);
+ assert_equals(detectedTextBlocks[0].boundingBox, detectedTextBlocks[0].boundingBox);
+}
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/idlharness.https.any.js b/testing/web-platform/tests/shape-detection/idlharness.https.any.js
new file mode 100644
index 0000000000..33b5f88f75
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/idlharness.https.any.js
@@ -0,0 +1,19 @@
+// META: global=window,worker
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+// See: https://wicg.github.io/shape-detection-api/
+
+'use strict';
+
+idl_test(
+ ['shape-detection-api', 'text-detection-api'],
+ ['dom', 'geometry'],
+ async idl_array => {
+ idl_array.add_objects({
+ FaceDetector: ['new FaceDetector()'],
+ BarcodeDetector: ['new BarcodeDetector()'],
+ TextDetector: ['new TextDetector()'],
+ });
+ }
+);
diff --git a/testing/web-platform/tests/shape-detection/resources/aztec-correction.jpg b/testing/web-platform/tests/shape-detection/resources/aztec-correction.jpg
new file mode 100644
index 0000000000..55bc0d23d6
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/aztec-correction.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/aztec-full.jpg b/testing/web-platform/tests/shape-detection/resources/aztec-full.jpg
new file mode 100644
index 0000000000..36390f0821
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/aztec-full.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/aztec-layers.jpg b/testing/web-platform/tests/shape-detection/resources/aztec-layers.jpg
new file mode 100644
index 0000000000..75449a7044
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/aztec-layers.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/aztec.jpg b/testing/web-platform/tests/shape-detection/resources/aztec.jpg
new file mode 100644
index 0000000000..f71e11f064
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/aztec.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/barcodes.mov b/testing/web-platform/tests/shape-detection/resources/barcodes.mov
new file mode 100644
index 0000000000..473a7ae239
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/barcodes.mov
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/code128-height.jpg b/testing/web-platform/tests/shape-detection/resources/code128-height.jpg
new file mode 100644
index 0000000000..69883e5683
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/code128-height.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/code128.jpg b/testing/web-platform/tests/shape-detection/resources/code128.jpg
new file mode 100644
index 0000000000..9bb66fe8ca
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/code128.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpg b/testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpg
new file mode 100644
index 0000000000..1fb4e660e4
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpg b/testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpg
new file mode 100644
index 0000000000..ce8837851b
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/face-center.jpg b/testing/web-platform/tests/shape-detection/resources/face-center.jpg
new file mode 100644
index 0000000000..30364040ed
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/face-center.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/face-top-left.jpg b/testing/web-platform/tests/shape-detection/resources/face-top-left.jpg
new file mode 100644
index 0000000000..300866b2eb
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/face-top-left.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/face-top-right.jpg b/testing/web-platform/tests/shape-detection/resources/face-top-right.jpg
new file mode 100644
index 0000000000..528a2a778d
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/face-top-right.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/faces.mov b/testing/web-platform/tests/shape-detection/resources/faces.mov
new file mode 100644
index 0000000000..b2c7b8c577
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/faces.mov
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpg
new file mode 100644
index 0000000000..54a08cf0ee
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpg
new file mode 100644
index 0000000000..1185c56dfe
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpg
new file mode 100644
index 0000000000..20e4a1c64c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpg
new file mode 100644
index 0000000000..0c64ea9605
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpg
new file mode 100644
index 0000000000..9f5f67b10c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-square.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-square.jpg
new file mode 100644
index 0000000000..8a379ae6cc
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-square.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpg
new file mode 100644
index 0000000000..a520f22fde
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpg
new file mode 100644
index 0000000000..9f5f67b10c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417.jpg
new file mode 100644
index 0000000000..ebbe36b510
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/pdf417.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpg b/testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpg
new file mode 100644
index 0000000000..754bdedaf1
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpg b/testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpg
new file mode 100644
index 0000000000..483297100d
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-center.jpg b/testing/web-platform/tests/shape-detection/resources/qr-center.jpg
new file mode 100644
index 0000000000..ca8e770bf4
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-center.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-h.jpg b/testing/web-platform/tests/shape-detection/resources/qr-h.jpg
new file mode 100644
index 0000000000..a1beb02101
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-h.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-l.jpg b/testing/web-platform/tests/shape-detection/resources/qr-l.jpg
new file mode 100644
index 0000000000..a29c358393
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-l.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-m.jpg b/testing/web-platform/tests/shape-detection/resources/qr-m.jpg
new file mode 100644
index 0000000000..ca8e770bf4
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-m.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-q.jpg b/testing/web-platform/tests/shape-detection/resources/qr-q.jpg
new file mode 100644
index 0000000000..45313c71b0
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-q.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-top-left.jpg b/testing/web-platform/tests/shape-detection/resources/qr-top-left.jpg
new file mode 100644
index 0000000000..f3092933e4
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-top-left.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/qr-top-right.jpg b/testing/web-platform/tests/shape-detection/resources/qr-top-right.jpg
new file mode 100644
index 0000000000..875ee8035c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/qr-top-right.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js b/testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js
new file mode 100644
index 0000000000..1b4949b8f6
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js
@@ -0,0 +1,75 @@
+'use strict';
+
+// These tests rely on the User Agent providing an implementation of
+// platform shape detection backends.
+//
+// In Chromium-based browsers this implementation is provided by a polyfill
+// in order to reduce the amount of test-only code shipped to users. To enable
+// these tests the browser must be run with these options:
+//
+// --enable-blink-features=MojoJS,MojoJSTest
+
+async function loadChromiumResources() {
+ await import('/resources/chromium/mock-barcodedetection.js');
+ await import('/resources/chromium/mock-facedetection.js');
+ await import('/resources/chromium/mock-textdetection.js');
+}
+
+/**
+ * @param {String} detectionTestName
+ * name of mock shape detection test interface,
+ * must be the item of ["FaceDetectionTest", "BarcodeDetectionTest",
+ * "TextDetectionTest"]
+*/
+async function initialize_detection_tests(detectionTestName) {
+ let detectionTest;
+ if (typeof document === 'undefined') {
+ // Use 'self' for workers.
+ if (typeof self[detectionTestName] === 'undefined') {
+ // test-only-api.js is already loaded in worker.js
+ if (isChromiumBased) {
+ await loadChromiumResources();
+ }
+ }
+ detectionTest = new self[detectionTestName]();
+ } else {
+ if (typeof window[detectionTestName] === 'undefined') {
+ const script = document.createElement('script');
+ script.src = '/resources/test-only-api.js';
+ script.async = false;
+ const p = new Promise((resolve, reject) => {
+ script.onload = () => { resolve(); };
+ script.onerror = e => { reject(e); };
+ })
+ document.head.appendChild(script);
+ await p;
+
+ if (isChromiumBased) {
+ await loadChromiumResources();
+ }
+
+ }
+ detectionTest = new window[detectionTestName]();
+ }
+ await detectionTest.initialize();
+ return detectionTest;
+}
+
+function detection_test(detectionTestName, func, name, properties) {
+ promise_test(async t => {
+ let detectionTest = await initialize_detection_tests(detectionTestName);
+ try {
+ await func(t, detectionTest);
+ } finally {
+ await detectionTest.reset();
+ };
+ }, name, properties);
+}
+
+function getArrayBufferFromBigBuffer(bigBuffer) {
+ if (bigBuffer.bytes !== undefined) {
+ return new Uint8Array(bigBuffer.bytes).buffer;
+ }
+ return bigBuffer.sharedMemory.bufferHandle.mapBuffer(0,
+ bigBuffer.sharedMemory.size).buffer;
+}
diff --git a/testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js b/testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js
new file mode 100644
index 0000000000..bbd2bda96b
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js
@@ -0,0 +1,66 @@
+function imageLoadedPromise(image) {
+ return new Promise(function(resolve, reject) {
+ if (image.complete)
+ resolve();
+ image.addEventListener("load", resolve, { once: true });
+ });
+}
+
+function videoLoadedPromise(video) {
+ return new Promise(function(resolve, reject) {
+ if (video.readyState == 4)
+ resolve();
+ else {
+ video.addEventListener("loadeddata", resolve, { once: true });
+ video.addEventListener("error", reject, { once: true });
+ }
+ });
+}
+
+function waitForNFrames(count) {
+ if (count <= 0)
+ return Promise.reject(new TypeError("count should be greater than 0!"));
+
+ return new Promise(resolve => {
+ function tick() {
+ (--count) ? requestAnimationFrame(tick) : resolve();
+ }
+ requestAnimationFrame(tick);
+ });
+}
+
+function seekTo(video, time) {
+ return new Promise(function(resolve, reject) {
+ video.addEventListener("seeked", async function() {
+ /* Work around flakiness in video players... */
+ await waitForNFrames(3);
+ resolve();
+ }, { once: true });
+ video.currentTime = time;
+ });
+}
+
+function checkBoundingBox(actual, expected, fuzziness) {
+ assert_equals(actual.constructor.name, "DOMRectReadOnly");
+ assert_approx_equals(actual.left, expected.left, fuzziness);
+ assert_approx_equals(actual.right, expected.right, fuzziness);
+ assert_approx_equals(actual.top, expected.top, fuzziness);
+ assert_approx_equals(actual.bottom, expected.bottom, fuzziness);
+}
+
+function checkPointsLieWithinBoundingBox(points, boundingBox) {
+ for (point of points) {
+ assert_between_inclusive(point.x, boundingBox.left, boundingBox.right);
+ assert_between_inclusive(point.y, boundingBox.top, boundingBox.bottom);
+ }
+}
+
+function checkPointIsNear(actual, expected, fuzzinessX, fuzzinessY) {
+ assert_approx_equals(actual.x, expected.x, fuzzinessX);
+ assert_approx_equals(actual.y, expected.y, fuzzinessY);
+}
+
+function checkPointsAreNear(actual, expected, fuzzinessX, fuzzinessY) {
+ for (point of actual)
+ checkPointIsNear(point, expected, fuzzinessX, fuzzinessY);
+}
diff --git a/testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpg b/testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpg
new file mode 100644
index 0000000000..12a0641fd3
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpg b/testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpg
new file mode 100644
index 0000000000..7ad8b71b6c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/text-center.jpg b/testing/web-platform/tests/shape-detection/resources/text-center.jpg
new file mode 100644
index 0000000000..69e8ce446c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/text-center.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/text-top-left.jpg b/testing/web-platform/tests/shape-detection/resources/text-top-left.jpg
new file mode 100644
index 0000000000..53ca0e0aaf
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/text-top-left.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/text-top-right.jpg b/testing/web-platform/tests/shape-detection/resources/text-top-right.jpg
new file mode 100644
index 0000000000..76a22a677c
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/text-top-right.jpg
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/resources/text.mov b/testing/web-platform/tests/shape-detection/resources/text.mov
new file mode 100644
index 0000000000..d50197b326
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/resources/text.mov
Binary files differ
diff --git a/testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html b/testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html
new file mode 100644
index 0000000000..c4e3c3fec7
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+// cross-origin resources
+const IMAGE_URL =
+ "https://{{domains[www1]}}:{{ports[https][0]}}/images/green.png";
+const VIDEO_URL =
+ "https://{{domains[www1]}}:{{ports[https][0]}}/media/white.webm";
+
+const crossOriginTests =
+ [
+ {
+ createDetector: () => { return new FaceDetector(); },
+ detectorType: "FaceDetector"
+ },
+ {
+ createDetector: () => { return new BarcodeDetector(); },
+ detectorType: "BarcodeDetector"
+ },
+ {
+ createDetector: () => { return new TextDetector(); },
+ detectorType: "TextDetector"
+ }
+ ];
+
+for (let crossOriginTest of crossOriginTests) {
+
+ // Verifies that Detector rejects a cross-origin HTMLImageElement.
+ promise_test(async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = IMAGE_URL;
+ await imgWatcher.wait_for("load");
+ const detector = crossOriginTest.createDetector();
+ promise_rejects_dom(t, "SecurityError", detector.detect(img));
+ }, crossOriginTest.detectorType
+ + " should reject cross-origin HTMLImageElements with a SecurityError.");
+
+ // Verifies that Detector rejects a cross-origin ImageBitmap.
+ promise_test(async t => {
+ const img = new Image();
+ const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+ img.src = IMAGE_URL;
+ await imgWatcher.wait_for("load");
+ const imgBitmap = await createImageBitmap(img);
+ const detector = crossOriginTest.createDetector();
+ promise_rejects_dom(t, "SecurityError", detector.detect(imgBitmap));
+ }, crossOriginTest.detectorType
+ + " should reject cross-origin ImageBitmaps with a SecurityError.");
+
+ // Verifies that Detector rejects a cross-origin HTMLVideoElement.
+ promise_test(async t => {
+ const video = document.createElement('video');
+ const videoWatcher = new EventWatcher(t, video, ["loadeddata", "error"]);
+ video.src = VIDEO_URL;
+ await videoWatcher.wait_for("loadeddata");
+ const detector = crossOriginTest.createDetector();
+ promise_rejects_dom(t, "SecurityError", detector.detect(video));
+ }, crossOriginTest.detectorType
+ + " should reject cross-origin HTMLVideoElements with a SecurityError.");
+
+}
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/shape-detection/single-barcode-detection.https.html b/testing/web-platform/tests/shape-detection/single-barcode-detection.https.html
new file mode 100644
index 0000000000..1f14e35153
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/single-barcode-detection.https.html
@@ -0,0 +1,396 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/single-detection-helpers.js"></script>
+<body>
+</body>
+<script>
+const imageTests = {
+ aztecCorrection: {
+ name: "aztec-correction.jpg",
+ format: "aztec",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 240, right: 559, top: 144, bottom: 454}, fuzziness: 15},
+ topLeft: {position: {x: 240, y: 144}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 559, y: 144}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 559, y: 454}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 240, y: 454}, fuzzinessX: 15, fuzzinessY: 15}},
+ aztecFull: {
+ name: "aztec-full.jpg",
+ format: "aztec",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 281, right: 518, top: 184, bottom: 414}, fuzziness: 15},
+ topLeft: {position: {x: 281, y: 184}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 518, y: 184}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 518, y: 414}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 281, y: 414}, fuzzinessX: 15, fuzzinessY: 15}},
+ aztecLayers: {
+ name: "aztec-layers.jpg",
+ format: "aztec",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 175, right: 625, top: 75, bottom: 525}, fuzziness: 15},
+ topLeft: {position: {x: 175, y: 75}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 625, y: 75}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 625, y: 525}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 175, y: 525}, fuzzinessX: 15, fuzzinessY: 15}},
+ aztec: {
+ name: "aztec.jpg",
+ format: "aztec",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 302, right: 497, top: 202, bottom: 397}, fuzziness: 15},
+ topLeft: {position: {x: 302, y: 202}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 497, y: 202}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 497, y: 397}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 302, y: 397}, fuzzinessX: 15, fuzzinessY: 15}},
+ code128Height: {
+ name: "code128-height.jpg",
+ format: "code_128",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 90, right: 711, top: 149, bottom: 449}, fuzziness: 15},
+ topLeft: {position: {x: 90, y: 149}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 711, y: 149}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 711, y: 450}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 90, y: 450}, fuzzinessX: 15, fuzzinessY: 15}},
+ code128: {
+ name: "code128.jpg",
+ format: "code_128",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 90, right: 710, top: 267, bottom: 332}, fuzziness: 15},
+ topLeft: {position: {x: 90, y: 267}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 710, y: 267}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 710, y: 332}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 90, y: 332}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Columns: {
+ name: "pdf417-columns.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 39, right: 755, top: 243, bottom: 356}, fuzziness: 15},
+ topLeft: {position: {x: 39, y: 243}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 755, y: 243}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 755, y: 356}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 39, y: 356}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Compact: {
+ name: "pdf417-compact.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 186, right: 786, top: 242, bottom: 359}, fuzziness: 15},
+ topLeft: {position: {x: 186, y: 242}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 786, y: 242}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 786, y: 359}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 186, y: 359}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Compaction: {
+ name: "pdf417-compaction.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 84, right: 712, top: 217, bottom: 382}, fuzziness: 15},
+ topLeft: {position: {x: 84, y: 217}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 712, y: 217}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 712, y: 382}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 84, y: 382}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Correction: {
+ name: "pdf417-correction.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 39, right: 755, top: 209, bottom: 390}, fuzziness: 15},
+ topLeft: {position: {x: 39, y: 209}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 755, y: 209}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 755, y: 390}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 39, y: 390}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Rows: {
+ name: "pdf417-rows.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 84, right: 712, top: 227, bottom: 360}, fuzziness: 15},
+ topLeft: {position: {x: 84, y: 227}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 712, y: 227}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 712, y: 360}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 84, y: 360}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Square: {
+ name: "pdf417-square.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 170, right: 621, top: 119, bottom: 480}, fuzziness: 15},
+ topLeft: {position: {x: 171, y: 119}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 621, y: 119}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 621, y: 480}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 170, y: 480}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Taller: {
+ name: "pdf417-taller.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 84, right: 713, top: 209, bottom: 390}, fuzziness: 15},
+ topLeft: {position: {x: 84, y: 209}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 713, y: 209}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 713, y: 390}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 84, y: 390}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417Wider: {
+ name: "pdf417-wider.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 84, right: 712, top: 227, bottom: 360}, fuzziness: 15},
+ topLeft: {position: {x: 84, y: 227}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 712, y: 227}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 712, y: 360}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 84, y: 360}, fuzzinessX: 15, fuzzinessY: 15}},
+ pdf417: {
+ name: "pdf417.jpg",
+ format: "pdf417",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 126, right: 666, top: 209, bottom: 390}, fuzziness: 15},
+ topLeft: {position: {x: 126, y: 209}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 666, y: 209}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 666, y: 390}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 126, y: 390}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrBottomLeft: {
+ name: "qr-bottom-left.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 75, right: 325, top: 325, bottom: 575}, fuzziness: 5},
+ topLeft: {position: {x: 75, y: 325}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 325, y: 325}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 325, y: 575}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 75, y: 575}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrBottomRight: {
+ name: "qr-bottom-right.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 475, right: 725, top: 325, bottom: 575}, fuzziness: 5},
+ topLeft: {position: {x: 475, y: 325}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 725, y: 325}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 725, y: 575}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 475, y: 575}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrCenter: {
+ name: "qr-center.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 277, right: 524, top: 172, bottom: 428}, fuzziness: 5},
+ topLeft: {position: {x: 277, y: 172}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 521, y: 172}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 524, y: 425}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 277, y: 428}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrH: {
+ name: "qr-h.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 235, right: 565, top: 135, bottom: 465}, fuzziness: 5},
+ topLeft: {position: {x: 235, y: 135}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 565, y: 135}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 565, y: 465}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 235, y: 465}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrL: {
+ name: "qr-l.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 275, right: 525, top: 175, bottom: 425}, fuzziness: 5},
+ topLeft: {position: {x: 275, y: 175}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 525, y: 175}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 525, y: 425}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 275, y: 425}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrM: {
+ name: "qr-m.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 277, right: 524, top: 172, bottom: 428}, fuzziness: 5},
+ topLeft: {position: {x: 277, y: 172}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 521, y: 172}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 524, y: 425}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 277, y: 428}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrQ: {
+ name: "qr-q.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 252, right: 548, top: 157, bottom: 444}, fuzziness: 5},
+ topLeft: {position: {x: 252, y: 157}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 548, y: 157}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 545, y: 444}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 252, y: 441}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrTopLeft: {
+ name: "qr-top-left.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 75, right: 325, top: 25, bottom: 275}, fuzziness: 5},
+ topLeft: {position: {x: 75, y: 25}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 325, y: 25}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 325, y: 275}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 75, y: 275}, fuzzinessX: 15, fuzzinessY: 15}},
+ qrTopRight: {
+ name: "qr-top-right.jpg",
+ format: "qr_code",
+ payload: "Barcode Detection is Fun!",
+ barcode: {boundingBox: {left: 475, right: 725, top: 25, bottom: 275}, fuzziness: 5},
+ topLeft: {position: {x: 475, y: 25}, fuzzinessX: 15, fuzzinessY: 15},
+ topRight: {position: {x: 725, y: 25}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomRight: {position: {x: 725, y: 275}, fuzzinessX: 15, fuzzinessY: 15},
+ bottomLeft: {position: {x: 475, y: 275}, fuzzinessX: 15, fuzzinessY: 15}}};
+
+const videoTests = {
+ "barcodes.mov": [
+ {time: 0.5, test: imageTests.aztecCorrection},
+ {time: 1.5, test: imageTests.aztecFull},
+ {time: 2.5, test: imageTests.aztecLayers},
+ {time: 3.5, test: imageTests.aztec},
+ {time: 4.5, test: imageTests.code128Height},
+ {time: 5.5, test: imageTests.code128},
+ {time: 6.5, test: imageTests.pdf417Columns},
+ {time: 7.5, test: imageTests.pdf417Compact},
+ {time: 8.5, test: imageTests.pdf417Compaction},
+ {time: 9.5, test: imageTests.pdf417Correction},
+ {time: 10.5, test: imageTests.pdf417Rows},
+ {time: 11.5, test: imageTests.pdf417Square},
+ {time: 12.5, test: imageTests.pdf417Taller},
+ {time: 13.5, test: imageTests.pdf417Wider},
+ {time: 14.5, test: imageTests.pdf417},
+ {time: 15.5, test: imageTests.qrBottomLeft},
+ {time: 16.5, test: imageTests.qrBottomRight},
+ {time: 17.5, test: imageTests.qrCenter},
+ {time: 18.5, test: imageTests.qrH},
+ {time: 19.5, test: imageTests.qrL},
+ {time: 20.5, test: imageTests.qrM},
+ {time: 21.5, test: imageTests.qrQ},
+ {time: 22.5, test: imageTests.qrTopLeft},
+ {time: 23.5, test: imageTests.qrTopRight}]};
+
+// All the fields in FaceDetectorOptions are hints, so they can't be tested.
+const barcodeDetector = new BarcodeDetector();
+
+async function testImage(imageBitmapSource, test, key) {
+ const supportedFormats = await BarcodeDetector.getSupportedFormats();
+ if (!supportedFormats.includes(test.format))
+ return;
+ const detectedBarcodes = await barcodeDetector.detect(imageBitmapSource);
+ assert_equals(detectedBarcodes.length, 1);
+ const detectedBarcode = detectedBarcodes[0];
+ checkBoundingBox(detectedBarcode.boundingBox, test.barcode.boundingBox, test.barcode.fuzziness);
+ assert_equals(detectedBarcode.rawValue, test.payload);
+ assert_equals(detectedBarcode.format, test.format);
+ assert_equals(detectedBarcode.cornerPoints.length, 4);
+ const [topLeft, topRight, bottomRight, bottomLeft] = detectedBarcode.cornerPoints;
+ checkPointIsNear(topLeft, test.topLeft.position, test.topLeft.fuzzinessX, test.topLeft.fuzzinessY);
+ checkPointIsNear(topRight, test.topRight.position, test.topRight.fuzzinessX, test.topRight.fuzzinessY);
+ checkPointIsNear(bottomRight, test.bottomRight.position, test.bottomRight.fuzzinessX, test.bottomRight.fuzzinessY);
+ checkPointIsNear(bottomLeft, test.bottomLeft.position, test.bottomLeft.fuzzinessX, test.bottomLeft.fuzzinessY);
+}
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ await testImage(imageElement, imageTest, key);
+ }
+}, "HTMLImageElement");
+
+// Intentionally don't test SVGImageElement. The spec https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource says it's supposed to be
+// a CanvasImageSource, but neither WebKit nor Blink actually seem to implement that.
+
+promise_test(async t => {
+ for (const [name, tests] of Object.entries(videoTests)) {
+ const videoElement = document.createElement("video");
+ document.body.appendChild(videoElement);
+ videoElement.src = `resources/${name}`;
+ const loadedPromise = videoLoadedPromise(videoElement);
+ videoElement.load();
+ await loadedPromise;
+ for (const test of tests) {
+ await seekTo(videoElement, test.time);
+ await testImage(videoElement, test.test, name);
+ }
+ document.body.removeChild(videoElement);
+ }
+}, "HTMLVideoElement");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ await testImage(canvasElement, imageTest, key);
+ }
+}, "HTMLCanvasElement");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const imageBitmap = await createImageBitmap(imageElement);
+ await testImage(imageBitmap, imageTest, key);
+ }
+}, "ImageBitmap");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const offscreenCanvas = new OffscreenCanvas(imageElement.width, imageElement.height);
+ const context = offscreenCanvas.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ await testImage(offscreenCanvas, imageTest, key);
+ }
+}, "OffscreenCanvas");
+
+promise_test(async t => {
+ for (const [name, tests] of Object.entries(videoTests)) {
+ const videoElement = document.createElement("video");
+ document.body.appendChild(videoElement);
+ videoElement.src = `resources/${name}`;
+ const loadedPromise = videoLoadedPromise(videoElement);
+ videoElement.load();
+ await loadedPromise;
+ for (const test of tests) {
+ await seekTo(videoElement, test.time);
+ const videoFrame = new VideoFrame(videoElement);
+ await testImage(videoFrame, test.test, name);
+ videoFrame.close();
+ }
+ document.body.removeChild(videoElement);
+ }
+}, "VideoFrame");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ const blob = await new Promise(function(resolve, reject) {
+ canvasElement.toBlob(function(blob) {
+ return resolve(blob);
+ });
+ });
+ await testImage(blob, imageTest, key);
+ }
+}, "Blob");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height);
+ await testImage(imageData, imageTest, key);
+ }
+}, "ImageData");
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/single-face-detection.https.html b/testing/web-platform/tests/shape-detection/single-face-detection.https.html
new file mode 100644
index 0000000000..28afdef136
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/single-face-detection.https.html
@@ -0,0 +1,223 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/single-detection-helpers.js"></script>
+<body>
+</body>
+<script>
+const imageTests = {
+ center: {
+ name: "face-center.jpg",
+ face: {boundingBox: {left: 312, right: 512, top: 238, bottom: 438}, fuzziness: 25},
+ mouth: {position: {x: 414, y: 379}, fuzzinessX: 30, fuzzinessY: 20},
+ leftEye: {position: {x: 378, y: 293}, fuzzinessX: 20, fuzzinessY: 10},
+ rightEye: {position: {x: 448, y: 292}, fuzzinessX: 20, fuzzinessY: 10},
+ nose: {position: {x: 412, y: 335}, fuzzinessX: 20, fuzzinessY: 35}},
+ bottomLeft: {
+ name: "face-bottom-left.jpg",
+ face: {boundingBox: {left: 96, right: 387, top: 281, bottom: 572}, fuzziness: 15},
+ mouth: {position: {x: 248, y: 483}, fuzzinessX: 45, fuzzinessY: 25},
+ leftEye: {position: {x: 196, y: 359}, fuzzinessX: 25, fuzzinessY: 10},
+ rightEye: {position: {x: 296, y: 357}, fuzzinessX: 25, fuzzinessY: 10},
+ nose: {position: {x: 244, y: 419}, fuzzinessX: 30, fuzzinessY: 50}},
+ bottomRight: {
+ name: "face-bottom-right.jpg",
+ face: {boundingBox: {left: 445, right: 733, top: 284, bottom: 572}, fuzziness: 10},
+ mouth: {position: {x: 593, y: 487}, fuzzinessX: 45, fuzzinessY: 25},
+ leftEye: {position: {x: 542, y: 363}, fuzzinessX: 25, fuzzinessY: 10},
+ rightEye: {position: {x: 641, y: 361}, fuzzinessX: 25, fuzzinessY: 10},
+ nose: {position: {x: 590, y: 423}, fuzzinessX: 30, fuzzinessY: 50}},
+ topLeft: {
+ name: "face-top-left.jpg",
+ face: {boundingBox: {left: 101, right: 387, top: 119, bottom: 405}, fuzziness: 10},
+ mouth: {position: {x: 246, y: 322}, fuzzinessX: 45, fuzzinessY: 25},
+ leftEye: {position: {x: 194, y: 198}, fuzzinessX: 25, fuzzinessY: 10},
+ rightEye: {position: {x: 295, y: 196}, fuzzinessX: 25, fuzzinessY: 10},
+ nose: {position: {x: 243, y: 258}, fuzzinessX: 30, fuzzinessY: 50}},
+ topRight: {
+ name: "face-top-right.jpg",
+ face: {boundingBox: {left: 451, right: 735, top: 124, bottom: 408}, fuzziness: 10},
+ mouth: {position: {x: 594, y: 326}, fuzzinessX: 45, fuzzinessY: 25},
+ leftEye: {position: {x: 542, y: 202}, fuzzinessX: 25, fuzzinessY: 10},
+ rightEye: {position: {x: 642, y: 200}, fuzzinessX: 25, fuzzinessY: 10},
+ nose: {position: {x: 591, y: 261}, fuzzinessX: 30, fuzzinessY: 50}}};
+
+const videoTests = {
+ "faces.mov": [
+ {time: 0.5, test: imageTests.center},
+ {time: 1.5, test: imageTests.bottomLeft},
+ {time: 2.5, test: imageTests.bottomRight},
+ {time: 3.5, test: imageTests.topLeft},
+ {time: 4.5, test: imageTests.topRight}]};
+
+// All the fields in FaceDetectorOptions are hints, so they can't be tested.
+const faceDetector = new FaceDetector();
+
+async function testImage(imageBitmapSource, test) {
+ const detectedFaces = await faceDetector.detect(imageBitmapSource);
+ assert_equals(detectedFaces.length, 1);
+ const detectedFace = detectedFaces[0];
+ checkBoundingBox(detectedFace.boundingBox, test.face.boundingBox, test.face.fuzziness);
+ if (detectedFace.landmarks) {
+ var mouthCount = 0;
+ var eyeCount = 0;
+ var noseCount = 0;
+ for (landmark of detectedFace.landmarks) {
+ checkPointsLieWithinBoundingBox(landmark.locations, detectedFace.boundingBox);
+ switch (landmark.type) {
+ case "mouth":
+ checkPointsAreNear(landmark.locations, test.mouth.position, test.mouth.fuzzinessX, test.mouth.fuzzinessY);
+ ++mouthCount;
+ break;
+ case "eye":
+ // handled below
+ ++eyeCount;
+ break;
+ case "nose":
+ checkPointsAreNear(landmark.locations, test.nose.position, test.nose.fuzzinessX, test.nose.fuzzinessY);
+ ++noseCount;
+ break;
+ default:
+ assert(false);
+ }
+ }
+ assert_less_than_equal(mouthCount, 1);
+ assert_true(eyeCount == 0 || eyeCount == 2, "There should be 2 eyes (or the implementation doesn't support detecting eyes)");
+ assert_less_than_equal(noseCount, 1);
+
+ const [leftEye, rightEye] = detectedFace.landmarks.filter(landmark => landmark.type == "eye").toSorted(function(landmarkA, landmarkB) {
+ // The left eye has a smaller X coordinate than the right eye.
+ const locationsA = landmarkA.locations.map(location => location.x);
+ const locationsB = landmarkB.locations.map(location => location.x);
+ const locationA = locationsA.reduce((a, b) => a + b) / locationsA.length;
+ const locationB = locationsB.reduce((a, b) => a + b) / locationsB.length;
+ return locationA - locationB;
+ });
+ checkPointsAreNear(leftEye.locations, test.leftEye.position, test.leftEye.fuzzinessX, test.leftEye.fuzzinessY);
+ checkPointsAreNear(rightEye.locations, test.rightEye.position, test.rightEye.fuzzinessX, test.rightEye.fuzzinessY);
+ }
+}
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ await testImage(imageElement, imageTest);
+ }
+}, "HTMLImageElement");
+
+// Intentionally don't test SVGImageElement. The spec https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource says it's supposed to be
+// a CanvasImageSource, but neither WebKit nor Blink actually seem to implement that.
+
+promise_test(async t => {
+ for (const [name, tests] of Object.entries(videoTests)) {
+ const videoElement = document.createElement("video");
+ document.body.appendChild(videoElement);
+ videoElement.src = `resources/${name}`;
+ const loadedPromise = videoLoadedPromise(videoElement);
+ videoElement.load();
+ await loadedPromise;
+ for (const test of tests) {
+ await seekTo(videoElement, test.time);
+ await testImage(videoElement, test.test);
+ }
+ document.body.removeChild(videoElement);
+ }
+}, "HTMLVideoElement");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ await testImage(canvasElement, imageTest);
+ }
+}, "HTMLCanvasElement");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const imageBitmap = await createImageBitmap(imageElement);
+ await testImage(imageBitmap, imageTest);
+ }
+}, "ImageBitmap");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const offscreenCanvas = new OffscreenCanvas(imageElement.width, imageElement.height);
+ const context = offscreenCanvas.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ await testImage(offscreenCanvas, imageTest);
+ }
+}, "OffscreenCanvas");
+
+promise_test(async t => {
+ for (const [name, tests] of Object.entries(videoTests)) {
+ const videoElement = document.createElement("video");
+ document.body.appendChild(videoElement);
+ videoElement.src = `resources/${name}`;
+ const loadedPromise = videoLoadedPromise(videoElement);
+ videoElement.load();
+ await loadedPromise;
+ for (const test of tests) {
+ await seekTo(videoElement, test.time);
+ const videoFrame = new VideoFrame(videoElement);
+ await testImage(videoFrame, test.test);
+ videoFrame.close();
+ }
+ document.body.removeChild(videoElement);
+ }
+}, "VideoFrame");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ const blob = await new Promise(function(resolve, reject) {
+ canvasElement.toBlob(function(blob) {
+ return resolve(blob);
+ });
+ });
+ await testImage(blob, imageTest);
+ }
+}, "Blob");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_true(imageElement.complete, "Image element should have loaded successfully");
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height);
+ await testImage(imageData, imageTest);
+ }
+}, "ImageData");
+
+</script>
diff --git a/testing/web-platform/tests/shape-detection/single-text-detection.https.html b/testing/web-platform/tests/shape-detection/single-text-detection.https.html
new file mode 100644
index 0000000000..829e8ce6d6
--- /dev/null
+++ b/testing/web-platform/tests/shape-detection/single-text-detection.https.html
@@ -0,0 +1,197 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/single-detection-helpers.js"></script>
+<body>
+</body>
+<script>
+const imageTests = {
+ center: {
+ name: "text-center.jpg",
+ value: "Dictionary",
+ text: {boundingBox: {left: 195, right: 613, top: 258, bottom: 358}, fuzziness: 7},
+ topLeft: {position: {x: 199, y: 258}, fuzzinessX: 10, fuzzinessY: 10},
+ topRight: {position: {x: 613, y: 281}, fuzzinessX: 10, fuzzinessY: 17},
+ bottomRight: {position: {x: 609, y: 358}, fuzzinessX: 10, fuzzinessY: 4},
+ bottomLeft: {position: {x: 195, y: 334}, fuzzinessX: 10, fuzzinessY: 22}},
+ bottomLeft: {
+ name: "text-bottom-left.jpg",
+ value: "Dictionary",
+ text: {boundingBox: {left: 53, right: 469, top: 461, bottom: 546}, fuzziness: 10},
+ topLeft: {position: {x: 53, y: 461}, fuzzinessX: 10, fuzzinessY: 20},
+ topRight: {position: {x: 469, y: 463}, fuzzinessX: 10, fuzzinessY: 20},
+ bottomRight: {position: {x: 469, y: 546}, fuzzinessX: 10, fuzzinessY: 17},
+ bottomLeft: {position: {x: 53, y: 544}, fuzzinessX: 10, fuzzinessY: 25}},
+ bottomRight: {
+ name: "text-bottom-right.jpg",
+ value: "Dictionary",
+ text: {boundingBox: {left: 357, right: 772, top: 471, bottom: 564}, fuzziness: 10},
+ topLeft: {position: {x: 358, y: 471}, fuzzinessX: 10, fuzzinessY: 20},
+ topRight: {position: {x: 772, y: 476}, fuzzinessX: 10, fuzzinessY: 20},
+ bottomRight: {position: {x: 771, y: 564}, fuzzinessX: 10, fuzzinessY: 17},
+ bottomLeft: {position: {x: 357, y: 559}, fuzzinessX: 10, fuzzinessY: 25}},
+ topLeft: {
+ name: "text-top-left.jpg",
+ value: "Dictionary",
+ text: {boundingBox: {left: 53, right: 474, top: 81, bottom: 182}, fuzziness: 10},
+ topLeft: {position: {x: 58, y: 81}, fuzzinessX: 10, fuzzinessY: 20},
+ topRight: {position: {x: 474, y: 105}, fuzzinessX: 10, fuzzinessY: 20},
+ bottomRight: {position: {x: 470, y: 182}, fuzzinessX: 10, fuzzinessY: 17},
+ bottomLeft: {position: {x: 53, y: 158}, fuzzinessX: 10, fuzzinessY: 25}},
+ topRight: {
+ name: "text-top-right.jpg",
+ value: "Dictionary",
+ text: {boundingBox: {left: 343, right: 761, top: 66, bottom: 146}, fuzziness: 10},
+ topLeft: {position: {x: 343, y: 66}, fuzzinessX: 10, fuzzinessY: 20},
+ topRight: {position: {x: 761, y: 69}, fuzzinessX: 10, fuzzinessY: 20},
+ bottomRight: {position: {x: 761, y: 146}, fuzzinessX: 10, fuzzinessY: 17},
+ bottomLeft: {position: {x: 343, y: 143}, fuzzinessX: 10, fuzzinessY: 25}}};
+
+const videoTests = {
+ "text.mov": [
+ {time: 0.5, test: imageTests.center},
+ {time: 1.5, test: imageTests.bottomLeft},
+ {time: 2.5, test: imageTests.bottomRight},
+ {time: 3.5, test: imageTests.topLeft},
+ {time: 4.5, test: imageTests.topRight}]};
+
+// The TextDetector contructor doesn't take any options.
+const textDetector = new TextDetector();
+
+async function testImage(imageBitmapSource, test) {
+ var detectedText = await textDetector.detect(imageBitmapSource);
+ assert_equals(detectedText.length, 1);
+ detectedText = detectedText[0];
+ assert_equals(detectedText.rawValue, test.value);
+ checkBoundingBox(detectedText.boundingBox, test.text.boundingBox, test.text.fuzziness);
+ assert_equals(detectedText.cornerPoints.length, 4);
+ const [topLeft, topRight, bottomRight, bottomLeft] = detectedText.cornerPoints;
+ checkPointIsNear(topLeft, test.topLeft.position, test.topLeft.fuzzinessX, test.topLeft.fuzzinessY);
+ checkPointIsNear(topRight, test.topRight.position, test.topRight.fuzzinessX, test.topRight.fuzzinessY);
+ checkPointIsNear(bottomRight, test.bottomRight.position, test.bottomRight.fuzzinessX, test.bottomRight.fuzzinessY);
+ checkPointIsNear(bottomLeft, test.bottomLeft.position, test.bottomLeft.fuzzinessX, test.bottomLeft.fuzzinessY);
+}
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_equals(imageElement.complete, true);
+ await testImage(imageElement, imageTest);
+ }
+}, "HTMLImageElement");
+
+// Intentionally don't test SVGImageElement. The spec https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource says it's supposed to be
+// a CanvasImageSource, but neither WebKit nor Blink actually seem to implement that.
+
+promise_test(async t => {
+ for (const [name, tests] of Object.entries(videoTests)) {
+ const videoElement = document.createElement("video");
+ document.body.appendChild(videoElement);
+ videoElement.src = `resources/${name}`;
+ const loadedPromise = videoLoadedPromise(videoElement);
+ videoElement.load();
+ await loadedPromise;
+ for (const test of tests) {
+ await seekTo(videoElement, test.time);
+ await testImage(videoElement, test.test);
+ }
+ document.body.removeChild(videoElement);
+ }
+}, "HTMLVideoElement");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_equals(imageElement.complete, true);
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ await testImage(canvasElement, imageTest);
+ }
+}, "HTMLCanvasElement");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_equals(imageElement.complete, true);
+ const imageBitmap = await createImageBitmap(imageElement);
+ await testImage(imageBitmap, imageTest);
+ }
+}, "ImageBitmap");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_equals(imageElement.complete, true);
+ const offscreenCanvas = new OffscreenCanvas(imageElement.width, imageElement.height);
+ const context = offscreenCanvas.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ await testImage(offscreenCanvas, imageTest);
+ }
+}, "OffscreenCanvas");
+
+promise_test(async t => {
+ for (const [name, tests] of Object.entries(videoTests)) {
+ const videoElement = document.createElement("video");
+ document.body.appendChild(videoElement);
+ videoElement.src = `resources/${name}`;
+ const loadedPromise = videoLoadedPromise(videoElement);
+ videoElement.load();
+ await loadedPromise;
+ for (const test of tests) {
+ await seekTo(videoElement, test.time);
+ const videoFrame = new VideoFrame(videoElement);
+ await testImage(videoFrame, test.test);
+ videoFrame.close();
+ }
+ document.body.removeChild(videoElement);
+ }
+}, "VideoFrame");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_equals(imageElement.complete, true);
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ const blob = await new Promise(function(resolve, reject) {
+ canvasElement.toBlob(function(blob) {
+ return resolve(blob);
+ });
+ });
+ await testImage(blob, imageTest);
+ }
+}, "Blob");
+
+promise_test(async t => {
+ for (const [key, imageTest] of Object.entries(imageTests)) {
+ const imageElement = document.createElement("img");
+ imageElement.src = `resources/${imageTest.name}`;
+ await imageLoadedPromise(imageElement);
+ assert_equals(imageElement.complete, true);
+ const canvasElement = document.createElement("canvas");
+ canvasElement.width = imageElement.width;
+ canvasElement.height = imageElement.height;
+ const context = canvasElement.getContext("2d");
+ context.drawImage(imageElement, 0, 0);
+ const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height);
+ await testImage(imageData, imageTest);
+ }
+}, "ImageData");
+
+</script>