diff --git a/packages/core/src/textures/BaseTexture.js b/packages/core/src/textures/BaseTexture.js index 7e859f8..f3fa6a1 100644 --- a/packages/core/src/textures/BaseTexture.js +++ b/packages/core/src/textures/BaseTexture.js @@ -257,6 +257,7 @@ * @protected * @event PIXI.BaseTexture#error * @param {PIXI.BaseTexture} baseTexture - Resource errored. + * @param {ErrorEvent} event - Load error event. */ /** @@ -268,14 +269,6 @@ */ /** - * Fired when BaseTexture is destroyed. - * - * @protected - * @event PIXI.BaseTexture#error - * @param {PIXI.BaseTexture} baseTexture - Resource errored. - */ - - /** * Fired when BaseTexture is updated. * * @protected @@ -473,6 +466,16 @@ } /** + * Handle errors with resources. + * @private + * @param {ErrorEvent} event - Error event emitted. + */ + onError(event) + { + this.emit('error', this, event); + } + + /** * Destroys this base texture. * The method stops if resource doesn't want this texture to be destroyed. * Removes texture from all caches. diff --git a/packages/core/src/textures/BaseTexture.js b/packages/core/src/textures/BaseTexture.js index 7e859f8..f3fa6a1 100644 --- a/packages/core/src/textures/BaseTexture.js +++ b/packages/core/src/textures/BaseTexture.js @@ -257,6 +257,7 @@ * @protected * @event PIXI.BaseTexture#error * @param {PIXI.BaseTexture} baseTexture - Resource errored. + * @param {ErrorEvent} event - Load error event. */ /** @@ -268,14 +269,6 @@ */ /** - * Fired when BaseTexture is destroyed. - * - * @protected - * @event PIXI.BaseTexture#error - * @param {PIXI.BaseTexture} baseTexture - Resource errored. - */ - - /** * Fired when BaseTexture is updated. * * @protected @@ -473,6 +466,16 @@ } /** + * Handle errors with resources. + * @private + * @param {ErrorEvent} event - Error event emitted. + */ + onError(event) + { + this.emit('error', this, event); + } + + /** * Destroys this base texture. * The method stops if resource doesn't want this texture to be destroyed. * Removes texture from all caches. diff --git a/packages/core/src/textures/resources/ImageResource.js b/packages/core/src/textures/resources/ImageResource.js index f457f55..c490f41 100644 --- a/packages/core/src/textures/resources/ImageResource.js +++ b/packages/core/src/textures/resources/ImageResource.js @@ -152,6 +152,7 @@ else { source.onload = completed; + source.onerror = (event) => this.onError.run(event); } }); @@ -261,6 +262,9 @@ */ dispose() { + this.source.onload = null; + this.source.onerror = null; + super.dispose(); if (this.bitmap) diff --git a/packages/core/src/textures/BaseTexture.js b/packages/core/src/textures/BaseTexture.js index 7e859f8..f3fa6a1 100644 --- a/packages/core/src/textures/BaseTexture.js +++ b/packages/core/src/textures/BaseTexture.js @@ -257,6 +257,7 @@ * @protected * @event PIXI.BaseTexture#error * @param {PIXI.BaseTexture} baseTexture - Resource errored. + * @param {ErrorEvent} event - Load error event. */ /** @@ -268,14 +269,6 @@ */ /** - * Fired when BaseTexture is destroyed. - * - * @protected - * @event PIXI.BaseTexture#error - * @param {PIXI.BaseTexture} baseTexture - Resource errored. - */ - - /** * Fired when BaseTexture is updated. * * @protected @@ -473,6 +466,16 @@ } /** + * Handle errors with resources. + * @private + * @param {ErrorEvent} event - Error event emitted. + */ + onError(event) + { + this.emit('error', this, event); + } + + /** * Destroys this base texture. * The method stops if resource doesn't want this texture to be destroyed. * Removes texture from all caches. diff --git a/packages/core/src/textures/resources/ImageResource.js b/packages/core/src/textures/resources/ImageResource.js index f457f55..c490f41 100644 --- a/packages/core/src/textures/resources/ImageResource.js +++ b/packages/core/src/textures/resources/ImageResource.js @@ -152,6 +152,7 @@ else { source.onload = completed; + source.onerror = (event) => this.onError.run(event); } }); @@ -261,6 +262,9 @@ */ dispose() { + this.source.onload = null; + this.source.onerror = null; + super.dispose(); if (this.bitmap) diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js index 87fc899..36b30e5 100644 --- a/packages/core/src/textures/resources/Resource.js +++ b/packages/core/src/textures/resources/Resource.js @@ -63,6 +63,14 @@ * @private */ this.onUpdate = new Runner('update'); + + /** + * Handle internal errors, such as loading errors + * + * @member {Runner} + * @private + */ + this.onError = new Runner('onError', 1); } /** @@ -74,6 +82,7 @@ { this.onResize.add(baseTexture); this.onUpdate.add(baseTexture); + this.onError.add(baseTexture); // Call a resize immediate if we already // have the width and height of the resource @@ -92,6 +101,7 @@ { this.onResize.remove(baseTexture); this.onUpdate.remove(baseTexture); + this.onError.remove(baseTexture); } /** @@ -206,12 +216,14 @@ { if (!this.destroyed) { + this.destroyed = true; + this.dispose(); + this.onError.removeAll(); + this.onError = null; this.onResize.removeAll(); this.onResize = null; this.onUpdate.removeAll(); this.onUpdate = null; - this.destroyed = true; - this.dispose(); } } } diff --git a/packages/core/src/textures/BaseTexture.js b/packages/core/src/textures/BaseTexture.js index 7e859f8..f3fa6a1 100644 --- a/packages/core/src/textures/BaseTexture.js +++ b/packages/core/src/textures/BaseTexture.js @@ -257,6 +257,7 @@ * @protected * @event PIXI.BaseTexture#error * @param {PIXI.BaseTexture} baseTexture - Resource errored. + * @param {ErrorEvent} event - Load error event. */ /** @@ -268,14 +269,6 @@ */ /** - * Fired when BaseTexture is destroyed. - * - * @protected - * @event PIXI.BaseTexture#error - * @param {PIXI.BaseTexture} baseTexture - Resource errored. - */ - - /** * Fired when BaseTexture is updated. * * @protected @@ -473,6 +466,16 @@ } /** + * Handle errors with resources. + * @private + * @param {ErrorEvent} event - Error event emitted. + */ + onError(event) + { + this.emit('error', this, event); + } + + /** * Destroys this base texture. * The method stops if resource doesn't want this texture to be destroyed. * Removes texture from all caches. diff --git a/packages/core/src/textures/resources/ImageResource.js b/packages/core/src/textures/resources/ImageResource.js index f457f55..c490f41 100644 --- a/packages/core/src/textures/resources/ImageResource.js +++ b/packages/core/src/textures/resources/ImageResource.js @@ -152,6 +152,7 @@ else { source.onload = completed; + source.onerror = (event) => this.onError.run(event); } }); @@ -261,6 +262,9 @@ */ dispose() { + this.source.onload = null; + this.source.onerror = null; + super.dispose(); if (this.bitmap) diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js index 87fc899..36b30e5 100644 --- a/packages/core/src/textures/resources/Resource.js +++ b/packages/core/src/textures/resources/Resource.js @@ -63,6 +63,14 @@ * @private */ this.onUpdate = new Runner('update'); + + /** + * Handle internal errors, such as loading errors + * + * @member {Runner} + * @private + */ + this.onError = new Runner('onError', 1); } /** @@ -74,6 +82,7 @@ { this.onResize.add(baseTexture); this.onUpdate.add(baseTexture); + this.onError.add(baseTexture); // Call a resize immediate if we already // have the width and height of the resource @@ -92,6 +101,7 @@ { this.onResize.remove(baseTexture); this.onUpdate.remove(baseTexture); + this.onError.remove(baseTexture); } /** @@ -206,12 +216,14 @@ { if (!this.destroyed) { + this.destroyed = true; + this.dispose(); + this.onError.removeAll(); + this.onError = null; this.onResize.removeAll(); this.onResize = null; this.onUpdate.removeAll(); this.onUpdate = null; - this.destroyed = true; - this.dispose(); } } } diff --git a/packages/core/src/textures/resources/SVGResource.js b/packages/core/src/textures/resources/SVGResource.js index 2a161fd..3cc2f38 100644 --- a/packages/core/src/textures/resources/SVGResource.js +++ b/packages/core/src/textures/resources/SVGResource.js @@ -123,6 +123,12 @@ BaseImageResource.crossOrigin(tempImage, this.svg, this._crossorigin); tempImage.src = this.svg; + tempImage.onerror = (event) => + { + tempImage.onerror = null; + this.onError.run(event); + }; + tempImage.onload = () => { const svgWidth = tempImage.width; diff --git a/packages/core/src/textures/BaseTexture.js b/packages/core/src/textures/BaseTexture.js index 7e859f8..f3fa6a1 100644 --- a/packages/core/src/textures/BaseTexture.js +++ b/packages/core/src/textures/BaseTexture.js @@ -257,6 +257,7 @@ * @protected * @event PIXI.BaseTexture#error * @param {PIXI.BaseTexture} baseTexture - Resource errored. + * @param {ErrorEvent} event - Load error event. */ /** @@ -268,14 +269,6 @@ */ /** - * Fired when BaseTexture is destroyed. - * - * @protected - * @event PIXI.BaseTexture#error - * @param {PIXI.BaseTexture} baseTexture - Resource errored. - */ - - /** * Fired when BaseTexture is updated. * * @protected @@ -473,6 +466,16 @@ } /** + * Handle errors with resources. + * @private + * @param {ErrorEvent} event - Error event emitted. + */ + onError(event) + { + this.emit('error', this, event); + } + + /** * Destroys this base texture. * The method stops if resource doesn't want this texture to be destroyed. * Removes texture from all caches. diff --git a/packages/core/src/textures/resources/ImageResource.js b/packages/core/src/textures/resources/ImageResource.js index f457f55..c490f41 100644 --- a/packages/core/src/textures/resources/ImageResource.js +++ b/packages/core/src/textures/resources/ImageResource.js @@ -152,6 +152,7 @@ else { source.onload = completed; + source.onerror = (event) => this.onError.run(event); } }); @@ -261,6 +262,9 @@ */ dispose() { + this.source.onload = null; + this.source.onerror = null; + super.dispose(); if (this.bitmap) diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js index 87fc899..36b30e5 100644 --- a/packages/core/src/textures/resources/Resource.js +++ b/packages/core/src/textures/resources/Resource.js @@ -63,6 +63,14 @@ * @private */ this.onUpdate = new Runner('update'); + + /** + * Handle internal errors, such as loading errors + * + * @member {Runner} + * @private + */ + this.onError = new Runner('onError', 1); } /** @@ -74,6 +82,7 @@ { this.onResize.add(baseTexture); this.onUpdate.add(baseTexture); + this.onError.add(baseTexture); // Call a resize immediate if we already // have the width and height of the resource @@ -92,6 +101,7 @@ { this.onResize.remove(baseTexture); this.onUpdate.remove(baseTexture); + this.onError.remove(baseTexture); } /** @@ -206,12 +216,14 @@ { if (!this.destroyed) { + this.destroyed = true; + this.dispose(); + this.onError.removeAll(); + this.onError = null; this.onResize.removeAll(); this.onResize = null; this.onUpdate.removeAll(); this.onUpdate = null; - this.destroyed = true; - this.dispose(); } } } diff --git a/packages/core/src/textures/resources/SVGResource.js b/packages/core/src/textures/resources/SVGResource.js index 2a161fd..3cc2f38 100644 --- a/packages/core/src/textures/resources/SVGResource.js +++ b/packages/core/src/textures/resources/SVGResource.js @@ -123,6 +123,12 @@ BaseImageResource.crossOrigin(tempImage, this.svg, this._crossorigin); tempImage.src = this.svg; + tempImage.onerror = (event) => + { + tempImage.onerror = null; + this.onError.run(event); + }; + tempImage.onload = () => { const svgWidth = tempImage.width; diff --git a/packages/core/src/textures/resources/VideoResource.js b/packages/core/src/textures/resources/VideoResource.js index fa07b15..e04f69f 100644 --- a/packages/core/src/textures/resources/VideoResource.js +++ b/packages/core/src/textures/resources/VideoResource.js @@ -93,6 +93,7 @@ // Bind for listeners this._onCanPlay = this._onCanPlay.bind(this); + this._onError = this._onError.bind(this); if (options.autoLoad !== false) { @@ -149,6 +150,7 @@ { source.addEventListener('canplay', this._onCanPlay); source.addEventListener('canplaythrough', this._onCanPlay); + source.addEventListener('error', this._onError, true); } else { @@ -173,6 +175,17 @@ } /** + * Handle video error events. + * + * @private + */ + _onError() + { + this.source.removeEventListener('error', this._onError, true); + this.onError.run(event); + } + + /** * Returns true if the underlying source is playing. * * @private @@ -276,6 +289,7 @@ if (this.source) { + this.source.removeEventListener('error', this._onError, true); this.source.pause(); this.source.src = ''; this.source.load(); diff --git a/packages/core/src/textures/BaseTexture.js b/packages/core/src/textures/BaseTexture.js index 7e859f8..f3fa6a1 100644 --- a/packages/core/src/textures/BaseTexture.js +++ b/packages/core/src/textures/BaseTexture.js @@ -257,6 +257,7 @@ * @protected * @event PIXI.BaseTexture#error * @param {PIXI.BaseTexture} baseTexture - Resource errored. + * @param {ErrorEvent} event - Load error event. */ /** @@ -268,14 +269,6 @@ */ /** - * Fired when BaseTexture is destroyed. - * - * @protected - * @event PIXI.BaseTexture#error - * @param {PIXI.BaseTexture} baseTexture - Resource errored. - */ - - /** * Fired when BaseTexture is updated. * * @protected @@ -473,6 +466,16 @@ } /** + * Handle errors with resources. + * @private + * @param {ErrorEvent} event - Error event emitted. + */ + onError(event) + { + this.emit('error', this, event); + } + + /** * Destroys this base texture. * The method stops if resource doesn't want this texture to be destroyed. * Removes texture from all caches. diff --git a/packages/core/src/textures/resources/ImageResource.js b/packages/core/src/textures/resources/ImageResource.js index f457f55..c490f41 100644 --- a/packages/core/src/textures/resources/ImageResource.js +++ b/packages/core/src/textures/resources/ImageResource.js @@ -152,6 +152,7 @@ else { source.onload = completed; + source.onerror = (event) => this.onError.run(event); } }); @@ -261,6 +262,9 @@ */ dispose() { + this.source.onload = null; + this.source.onerror = null; + super.dispose(); if (this.bitmap) diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js index 87fc899..36b30e5 100644 --- a/packages/core/src/textures/resources/Resource.js +++ b/packages/core/src/textures/resources/Resource.js @@ -63,6 +63,14 @@ * @private */ this.onUpdate = new Runner('update'); + + /** + * Handle internal errors, such as loading errors + * + * @member {Runner} + * @private + */ + this.onError = new Runner('onError', 1); } /** @@ -74,6 +82,7 @@ { this.onResize.add(baseTexture); this.onUpdate.add(baseTexture); + this.onError.add(baseTexture); // Call a resize immediate if we already // have the width and height of the resource @@ -92,6 +101,7 @@ { this.onResize.remove(baseTexture); this.onUpdate.remove(baseTexture); + this.onError.remove(baseTexture); } /** @@ -206,12 +216,14 @@ { if (!this.destroyed) { + this.destroyed = true; + this.dispose(); + this.onError.removeAll(); + this.onError = null; this.onResize.removeAll(); this.onResize = null; this.onUpdate.removeAll(); this.onUpdate = null; - this.destroyed = true; - this.dispose(); } } } diff --git a/packages/core/src/textures/resources/SVGResource.js b/packages/core/src/textures/resources/SVGResource.js index 2a161fd..3cc2f38 100644 --- a/packages/core/src/textures/resources/SVGResource.js +++ b/packages/core/src/textures/resources/SVGResource.js @@ -123,6 +123,12 @@ BaseImageResource.crossOrigin(tempImage, this.svg, this._crossorigin); tempImage.src = this.svg; + tempImage.onerror = (event) => + { + tempImage.onerror = null; + this.onError.run(event); + }; + tempImage.onload = () => { const svgWidth = tempImage.width; diff --git a/packages/core/src/textures/resources/VideoResource.js b/packages/core/src/textures/resources/VideoResource.js index fa07b15..e04f69f 100644 --- a/packages/core/src/textures/resources/VideoResource.js +++ b/packages/core/src/textures/resources/VideoResource.js @@ -93,6 +93,7 @@ // Bind for listeners this._onCanPlay = this._onCanPlay.bind(this); + this._onError = this._onError.bind(this); if (options.autoLoad !== false) { @@ -149,6 +150,7 @@ { source.addEventListener('canplay', this._onCanPlay); source.addEventListener('canplaythrough', this._onCanPlay); + source.addEventListener('error', this._onError, true); } else { @@ -173,6 +175,17 @@ } /** + * Handle video error events. + * + * @private + */ + _onError() + { + this.source.removeEventListener('error', this._onError, true); + this.onError.run(event); + } + + /** * Returns true if the underlying source is playing. * * @private @@ -276,6 +289,7 @@ if (this.source) { + this.source.removeEventListener('error', this._onError, true); this.source.pause(); this.source.src = ''; this.source.load(); diff --git a/packages/core/test/BaseTexture.js b/packages/core/test/BaseTexture.js index 137595c..635173a 100644 --- a/packages/core/test/BaseTexture.js +++ b/packages/core/test/BaseTexture.js @@ -1,6 +1,6 @@ const { BaseTextureCache, TextureCache } = require('@pixi/utils'); const { BaseTexture, Texture, RenderTexture, resources } = require('../'); -const { ImageResource } = resources; +const { ImageResource, SVGResource, VideoResource } = resources; const URL = 'foo.png'; const NAME = 'foo'; @@ -36,6 +36,57 @@ }); */ + it('should handle invalid image URL for textures', function (done) + { + cleanCache(); + + const invalidFile = 'missing-image.png'; + const baseTexture = BaseTexture.from(invalidFile); + + baseTexture.once('error', function (baseTexture, event) + { + expect(baseTexture.resource).to.be.instanceof(ImageResource); + expect(baseTexture.resource.url).contains(invalidFile); + expect(event.type).to.equal('error'); + baseTexture.destroy(); + done(); + }); + }); + + it('should handle invalid svg URL for textures', function (done) + { + cleanCache(); + + const invalidFile = 'missing-image.svg'; + const baseTexture = BaseTexture.from(invalidFile); + + baseTexture.once('error', function (baseTexture, event) + { + expect(baseTexture.resource).to.be.instanceof(SVGResource); + expect(baseTexture.resource.svg).contains(invalidFile); + expect(event.type).to.equal('error'); + baseTexture.destroy(); + done(); + }); + }); + + it('should handle invalid video URL for textures', function (done) + { + cleanCache(); + + const invalidFile = 'missing-image.mp4'; + const baseTexture = BaseTexture.from(invalidFile); + + baseTexture.once('error', function (baseTexture, event) + { + expect(baseTexture.resource).to.be.instanceof(VideoResource); + expect(baseTexture.resource.source.firstChild.src).contains(invalidFile); + expect(event.type).to.equal('error'); + baseTexture.destroy(); + done(); + }); + }); + it('should remove Canvas BaseTexture from cache on destroy', function () { cleanCache();