diff --git a/src/Feature.js b/src/Feature.js index b4af35f..c05fa9b 100644 --- a/src/Feature.js +++ b/src/Feature.js @@ -25,6 +25,21 @@ const testRangeBounds = document => { return false; }; +const testSVG = document => { + const img = new Image(); + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + img.src = `data:image/svg+xml,`; + + try { + ctx.drawImage(img, 0, 0); + canvas.toDataURL(); + } catch (e) { + return false; + } + return true; +}; + const FEATURES = { // $FlowFixMe - get/set properties not yet supported get SUPPORT_RANGE_BOUNDS() { @@ -32,6 +47,12 @@ const FEATURES = { const value = testRangeBounds(document); Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {value}); return value; + }, + get SUPPORT_SVG_DRAWING() { + 'use strict'; + const value = testSVG(document); + Object.defineProperty(FEATURES, 'SUPPORT_SVG_DRAWING', {value}); + return value; } }; diff --git a/src/ImageLoader.js b/src/ImageLoader.js index 523e318..855a751 100644 --- a/src/ImageLoader.js +++ b/src/ImageLoader.js @@ -7,6 +7,8 @@ import type Logger from './Logger'; export type ImageElement = Image | HTMLCanvasElement; type ImageCache = {[string]: Promise}; +import FEATURES from './Feature'; + export default class ImageLoader { origin: string; options: Options; @@ -30,10 +32,20 @@ export default class ImageLoader { return src; } - if (this.options.allowTaint === true || this.isInlineImage(src) || this.isSameOrigin(src)) { - return this.addImage(src, src); - } else if (typeof this.options.proxy === 'string' && !this.isSameOrigin(src)) { - // TODO proxy + if (isSVG(src)) { + if (this.options.allowTaint === true || FEATURES.SUPPORT_SVG_DRAWING) { + return this.addImage(src, src); + } + } else { + if ( + this.options.allowTaint === true || + isInlineImage(src) || + this.isSameOrigin(src) + ) { + return this.addImage(src, src); + } else if (typeof this.options.proxy === 'string' && !this.isSameOrigin(src)) { + // TODO proxy + } } } @@ -43,10 +55,6 @@ export default class ImageLoader { return key; } - isInlineImage(src: string): boolean { - return /data:image\/.*;base64,/i.test(src); - } - hasImageInCache(key: string): boolean { return typeof this.cache[key] !== 'undefined'; } @@ -112,3 +120,11 @@ export class ImageStore { return index === -1 ? null : this._images[index]; } } + +const INLINE_SVG = /^data:image\/svg\+xml/i; +const INLINE_IMG = /^data:image\/.*;base64,/i; + +const isInlineImage = (src: string): boolean => INLINE_IMG.test(src); + +const isSVG = (src: string): boolean => + src.substr(-3).toLowerCase() === 'svg' || INLINE_SVG.test(src); diff --git a/tests/testrunner.js b/tests/testrunner.js index ffa9539..c13c026 100644 --- a/tests/testrunner.js +++ b/tests/testrunner.js @@ -3,7 +3,7 @@ import parseRefTest from '../scripts/parse-reftest'; import reftests from './reftests'; import querystring from 'querystring'; -const DOWNLOAD_REFTESTS = true; +const DOWNLOAD_REFTESTS = false; const query = querystring.parse(location.search.replace(/^\?/, '')); const downloadResult = (filename, data) => { @@ -126,7 +126,7 @@ const assertPath = (result, expected, desc) => { const delta = 10; - if (REFTEST) { + if (REFTEST && query.refTest === 'true') { const RESULTS = parseRefTest(result); REFTEST.forEach(({action, line, ...args}, i) => { const RESULT = RESULTS[i];