From f7d2d7536a03b58b99c3a67cd93b58d24f811425 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Sat, 22 Apr 2017 09:49:10 +0200 Subject: [PATCH] Fix failing instanceof when reading context `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is inside an iframe or when running in a protected environment. We could guess the types from their toString() value but let's keep things flexible and assume it's a sufficient condition if the item has a context2D which has item as `canvas`. --- src/platforms/platform.dom.js | 24 +++++++++++++++--------- test/jasmine.matchers.js | 4 ++-- test/specs/platform.dom.tests.js | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/platforms/platform.dom.js b/src/platforms/platform.dom.js index 317d4bfed..524bc346e 100644 --- a/src/platforms/platform.dom.js +++ b/src/platforms/platform.dom.js @@ -196,15 +196,21 @@ module.exports = function(Chart) { item = item.canvas; } - if (item instanceof HTMLCanvasElement) { - // To prevent canvas fingerprinting, some add-ons undefine the getContext - // method, for example: https://github.com/kkapsner/CanvasBlocker - // https://github.com/chartjs/Chart.js/issues/2807 - var context = item.getContext && item.getContext('2d'); - if (context instanceof CanvasRenderingContext2D) { - initCanvas(item, config); - return context; - } + // To prevent canvas fingerprinting, some add-ons undefine the getContext + // method, for example: https://github.com/kkapsner/CanvasBlocker + // https://github.com/chartjs/Chart.js/issues/2807 + var context = item && item.getContext && item.getContext('2d'); + + // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is + // inside an iframe or when running in a protected environment. We could guess the + // types from their toString() value but let's keep things flexible and assume it's + // a sufficient condition if the item has a context2D which has item as `canvas`. + // https://github.com/chartjs/Chart.js/issues/3887 + // https://github.com/chartjs/Chart.js/issues/4102 + // https://github.com/chartjs/Chart.js/issues/4152 + if (context && context.canvas === item) { + initCanvas(item, config); + return context; } return null; diff --git a/test/jasmine.matchers.js b/test/jasmine.matchers.js index 8a3c9e121..832fba35e 100644 --- a/test/jasmine.matchers.js +++ b/test/jasmine.matchers.js @@ -93,9 +93,9 @@ function toBeValidChart() { if (!(actual instanceof Chart)) { message = 'Expected ' + actual + ' to be an instance of Chart'; - } else if (!(actual.canvas instanceof HTMLCanvasElement)) { + } else if (Object.prototype.toString.call(actual.canvas) !== '[object HTMLCanvasElement]') { message = 'Expected canvas to be an instance of HTMLCanvasElement'; - } else if (!(actual.ctx instanceof CanvasRenderingContext2D)) { + } else if (Object.prototype.toString.call(actual.ctx) !== '[object CanvasRenderingContext2D]') { message = 'Expected context to be an instance of CanvasRenderingContext2D'; } else if (typeof actual.height !== 'number' || !isFinite(actual.height)) { message = 'Expected height to be a strict finite number'; diff --git a/test/specs/platform.dom.tests.js b/test/specs/platform.dom.tests.js index 898de9c48..7ca768307 100644 --- a/test/specs/platform.dom.tests.js +++ b/test/specs/platform.dom.tests.js @@ -79,6 +79,28 @@ describe('Platform.dom', function() { chart.destroy(); }); + + it('should accept a canvas from an iframe', function(done) { + var iframe = document.createElement('iframe'); + iframe.onload = function() { + var doc = iframe.contentDocument; + doc.body.innerHTML += ''; + var canvas = doc.getElementById('chart'); + var chart = new Chart(canvas); + + expect(chart).toBeValidChart(); + expect(chart.canvas).toBe(canvas); + expect(chart.ctx).toBe(canvas.getContext('2d')); + + chart.destroy(); + canvas.remove(); + iframe.remove(); + + done(); + }; + + document.body.appendChild(iframe); + }); }); describe('config.options.aspectRatio', function() { -- 2.47.3