Skip to content

Commit 8f30742

Browse files
committed
Fix #8: reject detection promise in case of error, assert argument type
1 parent f3e6f9b commit 8f30742

File tree

4 files changed

+100
-27
lines changed

4 files changed

+100
-27
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,10 @@ const detector = new BarcodeDetectorPolyfill({
113113
const barcodes = await detector.detect(source)
114114
```
115115

116-
`source` may be any object of which an [`ImageBitmap`](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap) can be obtained:
116+
`source` must be an object of which an [`ImageBitmap`](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap)
117+
can be obtained, or else a
118+
[`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError)
119+
will be thrown:
117120

118121
+ an `<img>` element ([`HTMLImageElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement))
119122
+ a `<video>` element ([`HTMLVideoElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement))
@@ -125,6 +128,8 @@ const barcodes = await detector.detect(source)
125128
+ a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or a
126129
[`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) with a content `type` of `image/*`
127130

131+
In addition, any object with a zero `width` or `height` property will be tolerated.
132+
128133
The detector processes the `source` in natural size, making detection results independent of the size rendered
129134
by the browser.
130135

@@ -145,6 +150,10 @@ Additional properties provided only by `BarcodeDetectorPolyfill`:
145150
+ `quality`: a positive integer indicating the barcode quality as seen by the detector,
146151
specific to each `format`
147152

153+
If an error occurs during barcode detection then the `detect()` method returns a rejected
154+
[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
155+
containing the error.
156+
148157

149158
### Typescript support
150159

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@undecaf/barcode-detector-polyfill",
3-
"version": "0.9.13",
3+
"version": "0.9.14",
44
"description": "A WebAssembly polyfill for the Barcode Detection API",
55
"keywords": [
66
"polyfill",

src/BarcodeDetectorPolyfill.ts

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -57,41 +57,54 @@ export class BarcodeDetectorPolyfill {
5757
* Scans an image for barcodes and returns a {@link Promise} for the result.
5858
*
5959
* @param {ImageBitmapSource} source the image to be scanned
60-
* @returns {Promise<Array<DetectedBarcode>>} the scan result as described for {@link BarcodeDetector}
60+
* @returns {Promise<Array<DetectedBarcode>>} the scan result as described for {@link BarcodeDetector},
61+
* or a rejected {@link Promise} containing the error
62+
* @throws {TypeError} if the argument is not an {@link ImageBitmapSource}
6163
*/
6264
// TODO Enable cache for video source, disable for others unless overridden in zbarConfig
6365
detect(source: ImageBitmapSource): Promise<Array<DetectedBarcode>> {
66+
// Assert the argument type
67+
if (!BarcodeDetectorPolyfill.isImageBitmapSource(source)) {
68+
throw new TypeError('BarcodeDetector.detect() argument is not an ImageBitmapSource')
69+
}
70+
6471
// Return an empty array immediately if the source is an object with any zero dimension,
6572
// see https://wicg.github.io/shape-detection-api/#image-sources-for-detection
6673
const intrinsic = BarcodeDetectorPolyfill.intrinsicDimensions(source)
6774
if ((intrinsic.width === 0) || (intrinsic.height === 0)) {
6875
return Promise.resolve([])
6976

7077
} else {
71-
return Promise
72-
.all([
73-
this.toImageData(source),
74-
this.getScanner()
75-
])
76-
77-
.then(fulfilled => {
78-
const
79-
imageData: ImageData = fulfilled[0],
80-
scanner: ZBarScanner = fulfilled[1];
81-
82-
// Configure the image cache if so requested
83-
if (typeof this.zbarConfig.enableCache !== 'undefined') {
84-
scanner.enableCache(this.zbarConfig.enableCache)
85-
}
86-
87-
return scanRGBABuffer(imageData.data, imageData.width, imageData.height, scanner)
88-
})
89-
90-
.then(symbols => {
91-
return symbols.map(symbol =>
92-
this.toBarcodeDetectorResult(symbol)
93-
)
94-
})
78+
try {
79+
return Promise
80+
.all([
81+
this.toImageData(source),
82+
this.getScanner()
83+
])
84+
85+
.then(fulfilled => {
86+
const
87+
imageData: ImageData = fulfilled[0],
88+
scanner: ZBarScanner = fulfilled[1];
89+
90+
// Configure the image cache if so requested
91+
if (typeof this.zbarConfig.enableCache !== 'undefined') {
92+
scanner.enableCache(this.zbarConfig.enableCache)
93+
}
94+
95+
return scanRGBABuffer(imageData.data, imageData.width, imageData.height, scanner)
96+
})
97+
98+
.then(symbols => {
99+
return symbols.map(symbol =>
100+
this.toBarcodeDetectorResult(symbol)
101+
)
102+
})
103+
104+
} catch (error) {
105+
// #8: return a rejected Promise if an exception occurred
106+
return Promise.reject(error)
107+
}
95108
}
96109

97110
}
@@ -205,6 +218,24 @@ export class BarcodeDetectorPolyfill {
205218
}
206219

207220

221+
/**
222+
* Type guard for {@link external:ImageBitmapSource} and any
223+
* object having zero width or height.
224+
*/
225+
private static isImageBitmapSource(source: any): source is ImageBitmapSource {
226+
return (source instanceof HTMLImageElement)
227+
|| (source instanceof HTMLVideoElement)
228+
|| (source instanceof HTMLCanvasElement)
229+
|| (source instanceof Blob)
230+
|| (source instanceof ImageData)
231+
|| (source instanceof CanvasRenderingContext2D)
232+
|| (source instanceof ImageBitmap)
233+
// Note the (lenient) equality operator
234+
|| (source && source.width == 0)
235+
|| (source && source.height == 0)
236+
}
237+
238+
208239
/**
209240
* Returns the intrinsic (as opposed to the rendered)
210241
* dimensions of an {@link external:ImageBitmapSource}.

tests/unit/20-detection.spec.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,39 @@ describe('BarcodeDetectorPolyfill detection', () => {
322322

323323
expect(barcodes).to.be.an('array').that.has.lengthOf(0)
324324
})
325+
});
326+
327+
328+
[undefined, null, '', 0, {}, { width: 1 }]
329+
.forEach(source => {
330+
it(`throws a TypeError if the detect() argument is ${source}`, async () => {
331+
const detector = new BarcodeDetectorPolyfill({formats: tests[0].format})
332+
try {
333+
detector.detect(source)
334+
335+
} catch (error) {
336+
expect(error instanceof TypeError).to.equal(true, `Exception is not a TypeError but ${error}`)
337+
return
338+
}
339+
340+
expect(false).to.equal(true, 'No exception was thrown')
341+
})
342+
});
343+
344+
345+
[
346+
document.createElement('img'),
347+
document.createElement('video'),
348+
document.createElement('canvas'),
349+
new Blob(),
350+
]
351+
.forEach(source => {
352+
it(`returns a rejected Promise for an invalid ${source}`, () => {
353+
const detector = new BarcodeDetectorPolyfill({formats: tests[0].format})
354+
detector.detect(source)
355+
.then((result) => expect(false).to.equal(true, `Promise not rejected but resolved with ${result}`))
356+
.catch(() => {})
357+
})
325358
})
326359

327360
})

0 commit comments

Comments
 (0)