Skip to content

Commit

Permalink
Switch to a per-chart platform
Browse files Browse the repository at this point in the history
  • Loading branch information
davidswinegar committed Jan 16, 2020
1 parent f8945ea commit 5335709
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 174 deletions.
7 changes: 7 additions & 0 deletions docs/getting-started/v3-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,10 @@ Animation system was completely rewritten in Chart.js v3. Each property can now

* The second parameter to `drawPoint` is now the full options object, so `style`, `rotation`, and `radius` are no longer passed explicitly

#### Platform

* `Chart.platform` no longer exists. Every chart instance now has a separate platform instance, `chart.platform`.
* `Chart.platforms` is an object that contains two usable platform classes, `BasicPlatform` and `DomPlatform`. It also contains `Platform`, a class that all platforms must extend from.
* If the canvas passed in is an instance of `OffscreenCanvas`, the `BasicPlatform` is automatically used.
* T override the platform class used in a chart instance, pass `platform: PlatformClass` in the config object. Note that the class should be passed, not an instance of the class.
* To disable CSS injection (necessary in some iframe contexts), `Chart.platform.disableCSSInjection = true` is no longer supported. Pass `disableCSSInjection: true` in the config object instead.
7 changes: 3 additions & 4 deletions samples/advanced/content-security-policy.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
var utils = Samples.utils;

// CSP: disable automatic style injection
Chart.platform.current.disableCSSInjection = true;

utils.srand(110);

function generateData() {
Expand Down Expand Up @@ -48,7 +45,9 @@ window.addEventListener('load', function() {
return (size / 24) * base;
}
}
}
},
// CSP: disable automatic style injection
disableCSSInjection: true
}
});
});
53 changes: 47 additions & 6 deletions src/core/core.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import defaults from './core.defaults';
import helpers from '../helpers/index';
import Interaction from './core.interaction';
import layouts from './core.layouts';
import platform from '../platforms/platform';
import {BasicPlatform, DomPlatform, Platform} from '../platforms/platforms';
import plugins from './core.plugins';
import scaleService from '../core/core.scaleService';
import Tooltip from './core.tooltip';
Expand Down Expand Up @@ -148,13 +148,34 @@ function onAnimationProgress(ctx) {
helpers.callback(animationOptions && animationOptions.onProgress, arguments, chart);
}

/**
* Chart.js can take a string id of a canvas element, a 2d context, or a canvas element itself.
* Attempt to unwrap the item passed into the chart constructor so that it is a canvas element (if possible).
*/
function unwrapItem(item) {
if (typeof document !== undefined && typeof item === 'string') {
item = document.getElementById(item);
} else if (item.length) {
// Support for array based queries (such as jQuery)
item = item[0];
}

if (item && item.canvas) {
// Support for any object associated to a canvas (including a context2d)
item = item.canvas;
}
return item;
}

class Chart {
constructor(item, config) {
const me = this;

config = initConfig(config);
const unwrappedItem = unwrapItem(item);
me.initializePlatform(unwrappedItem, config);

const context = platform.current.acquireContext(item, config);
const context = me.platform.acquireContext(unwrappedItem, config);
const canvas = context && context.canvas;
const height = canvas && canvas.height;
const width = canvas && canvas.width;
Expand Down Expand Up @@ -226,6 +247,26 @@ class Chart {
return me;
}

/**
* @private
*/
initializePlatform(item, config) {
const me = this;

if (config.platform) {
if (!(config.platform instanceof Platform)) {
throw new Error('If config.platform is used, it must be an instance of Chart.platforms.Platform or one of its descendants');
}
me.platform = new config.platform(config);
} else if (typeof window === 'undefined' || typeof document === 'undefined' || !item) {
me.platform = new BasicPlatform(config);
} else if (window.OffscreenCanvas && item instanceof window.OffscreenCanvas) {
me.platform = new BasicPlatform(config);
} else {
me.platform = new DomPlatform(config);
}
}

clear() {
helpers.canvas.clear(this);
return this;
Expand Down Expand Up @@ -851,7 +892,7 @@ class Chart {
if (canvas) {
me.unbindEvents();
helpers.canvas.clear(me);
platform.current.releaseContext(me.ctx);
me.platform.releaseContext(me.ctx);
me.canvas = null;
me.ctx = null;
}
Expand Down Expand Up @@ -880,7 +921,7 @@ class Chart {
};

helpers.each(me.options.events, function(type) {
platform.current.addEventListener(me, type, listener);
me.platform.addEventListener(me, type, listener);
listeners[type] = listener;
});

Expand All @@ -891,7 +932,7 @@ class Chart {
me.resize();
};

platform.current.addEventListener(me, 'resize', listener);
me.platform.addEventListener(me, 'resize', listener);
listeners.resize = listener;
}
}
Expand All @@ -908,7 +949,7 @@ class Chart {

delete me._listeners;
helpers.each(listeners, function(listener, type) {
platform.current.removeEventListener(me, type, listener);
me.platform.removeEventListener(me, type, listener);
});
}

Expand Down
6 changes: 2 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Element from './core/core.element';
import elements from './elements';
import Interaction from './core/core.interaction';
import layouts from './core/core.layouts';
import platform from './platforms/platform';
import platforms from './platforms/platforms';
import pluginsCore from './core/core.plugins';
import Scale from './core/core.scale';
import scaleService from './core/core.scaleService';
Expand All @@ -34,7 +34,7 @@ Chart.Element = Element;
Chart.elements = elements;
Chart.Interaction = Interaction;
Chart.layouts = layouts;
Chart.platform = platform;
Chart.platforms = platforms;
Chart.plugins = pluginsCore;
Chart.Scale = Scale;
Chart.scaleService = scaleService;
Expand All @@ -59,8 +59,6 @@ for (var k in plugins) {
}
}

Chart.platform.current.initialize();

if (typeof window !== 'undefined') {
window.Chart = Chart;
}
Expand Down
22 changes: 13 additions & 9 deletions src/platforms/platform.basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@

'use strict';

module.exports = {
type: 'basic',
acquireContext: function(item) {
if (item && item.canvas) {
// Support for any object associated to a canvas (including a context2d)
item = item.canvas;
}
import Platform from './platform';

return item && item.getContext('2d') || null;
/**
* Platform class for charts without access to the DOM or to many element properties
* This platform is used by default for any chart passed an OffscreenCanvas.
* @extends Platform
*/
export default class BasicPlatform extends Platform {
acquireContext(item) {
// 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
return item && item.getContext && item.getContext('2d') || null;
}
};
}
69 changes: 29 additions & 40 deletions src/platforms/platform.dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import helpers from '../helpers';
import stylesheet from './platform.dom.css';
import Platform from './platform';

var EXPANDO_KEY = '$chartjs';
var CSS_PREFIX = 'chartjs-';
Expand Down Expand Up @@ -312,31 +313,31 @@ function injectCSS(rootNode, css) {
}
}

export default {
type: 'dom',

/**
* When `true`, prevents the automatic injection of the stylesheet required to
* correctly detect when the chart is added to the DOM and then resized. This
* switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
* to be manually imported to make this library compatible with any CSP.
* See https://github.com/chartjs/Chart.js/issues/5208
*/
disableCSSInjection: false,

/**
* This property holds whether this platform is enabled for the current environment.
* Currently used by platform.js to select the proper implementation.
* @private
*/
_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
/**
* Platform class for charts that can access the DOM and global window/document properties
* @constructor
* @extends Platform
*/
export default class DomPlatform extends Platform {
constructor(options) {
super();

/**
* When `true`, prevents the automatic injection of the stylesheet required to
* correctly detect when the chart is added to the DOM and then resized. This
* switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
* to be manually imported to make this library compatible with any CSP.
* See https://github.com/chartjs/Chart.js/issues/5208
*/
this.disableCSSInjection = options.disableCSSInjection || false;
}

/**
* Initializes resources that depend on platform options.
* @param {HTMLCanvasElement} canvas - The Canvas element.
* @private
*/
_ensureLoaded: function(canvas) {
_ensureLoaded(canvas) {
if (!this.disableCSSInjection) {
// If the canvas is in a shadow DOM, then the styles must also be inserted
// into the same shadow DOM.
Expand All @@ -345,21 +346,9 @@ export default {
var targetNode = root.host ? root : document.head;
injectCSS(targetNode, stylesheet);
}
},

acquireContext: function(item, config) {
if (typeof item === 'string') {
item = document.getElementById(item);
} else if (item.length) {
// Support for array based queries (such as jQuery)
item = item[0];
}

if (item && item.canvas) {
// Support for any object associated to a canvas (including a context2d)
item = item.canvas;
}
}

acquireContext(item, config) {
// 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
Expand All @@ -381,9 +370,9 @@ export default {
}

return null;
},
}

releaseContext: function(context) {
releaseContext(context) {
const canvas = context.canvas;
if (!canvas[EXPANDO_KEY]) {
return;
Expand Down Expand Up @@ -412,9 +401,9 @@ export default {
canvas.width = canvas.width;

delete canvas[EXPANDO_KEY];
},
}

addEventListener: function(chart, type, listener) {
addEventListener(chart, type, listener) {
var canvas = chart.canvas;
if (type === 'resize') {
// Note: the resize event is not supported on all browsers.
Expand All @@ -429,9 +418,9 @@ export default {
}, chart);

addListener(canvas, type, proxy);
},
}

removeEventListener: function(chart, type, listener) {
removeEventListener(chart, type, listener) {
var canvas = chart.canvas;
if (type === 'resize') {
// Note: the resize event is not supported on all browsers.
Expand All @@ -448,4 +437,4 @@ export default {

removeListener(canvas, type, proxy);
}
};
}
Loading

0 comments on commit 5335709

Please sign in to comment.