[Web] Add feature detection helpers to JS Engine class.

This commit is contained in:
Fabio Alessandrelli 2022-09-17 10:16:34 +02:00
parent 57bdddce02
commit 6bbde346ab
5 changed files with 108 additions and 17 deletions

View file

@ -5,6 +5,7 @@ module.exports = {
"globals": {
"InternalConfig": true,
"Godot": true,
"Features": true,
"Preloader": true,
},
};

View file

@ -66,6 +66,7 @@ sys_env.Depends(build[0], sys_env["JS_PRE"])
sys_env.Depends(build[0], sys_env["JS_EXTERNS"])
engine = [
"js/engine/features.js",
"js/engine/preloader.js",
"js/engine/config.js",
"js/engine/engine.js",

View file

@ -60,20 +60,6 @@ const Engine = (function () {
loadPromise = null;
};
/**
* Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for.
*
* @param {number=} [majorVersion=1] The major WebGL version to check for.
* @returns {boolean} If the given major version of WebGL is available.
* @function Engine.isWebGLAvailable
*/
Engine.isWebGLAvailable = function (majorVersion = 1) {
try {
return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]);
} catch (e) { /* Not available */ }
return false;
};
/**
* Safe Engine constructor, creates a new prototype for every new instance to avoid prototype pollution.
* @ignore
@ -265,14 +251,21 @@ const Engine = (function () {
// Also expose static methods as instance methods
Engine.prototype['load'] = Engine.load;
Engine.prototype['unload'] = Engine.unload;
Engine.prototype['isWebGLAvailable'] = Engine.isWebGLAvailable;
return new Engine(initConfig);
}
// Closure compiler exported static methods.
SafeEngine['load'] = Engine.load;
SafeEngine['unload'] = Engine.unload;
SafeEngine['isWebGLAvailable'] = Engine.isWebGLAvailable;
// Feature-detection utilities.
SafeEngine['isWebGLAvailable'] = Features.isWebGLAvailable;
SafeEngine['isFetchAvailable'] = Features.isFetchAvailable;
SafeEngine['isSecureContext'] = Features.isSecureContext;
SafeEngine['isCrossOriginIsolated'] = Features.isCrossOriginIsolated;
SafeEngine['isSharedArrayBufferAvailable'] = Features.isSharedArrayBufferAvailable;
SafeEngine['isAudioWorkletAvailable'] = Features.isAudioWorkletAvailable;
SafeEngine['getMissingFeatures'] = Features.getMissingFeatures;
return SafeEngine;
}());

View file

@ -0,0 +1,96 @@
const Features = { // eslint-disable-line no-unused-vars
/**
* Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for.
*
* @param {number=} [majorVersion=1] The major WebGL version to check for.
* @returns {boolean} If the given major version of WebGL is available.
* @function Engine.isWebGLAvailable
*/
isWebGLAvailable: function (majorVersion = 1) {
try {
return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]);
} catch (e) { /* Not available */ }
return false;
},
/**
* Check whether the Fetch API available and supports streaming responses.
*
* @returns {boolean} If the Fetch API is available and supports streaming responses.
* @function Engine.isFetchAvailable
*/
isFetchAvailable: function () {
return 'fetch' in window && 'Response' in window && 'body' in window.Response.prototype;
},
/**
* Check whether the engine is running in a Secure Context.
*
* @returns {boolean} If the engine is running in a Secure Context.
* @function Engine.isSecureContext
*/
isSecureContext: function () {
return window['isSecureContext'] === true;
},
/**
* Check whether the engine is cross origin isolated.
* This value is dependent on Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers sent by the server.
*
* @returns {boolean} If the engine is running in a Secure Context.
* @function Engine.isSecureContext
*/
isCrossOriginIsolated: function () {
return window['crossOriginIsolated'] === true;
},
/**
* Check whether SharedBufferArray is available.
*
* Most browsers require the page to be running in a secure context, and the
* the server to provide specific CORS headers for SharedArrayBuffer to be available.
*
* @returns {boolean} If SharedArrayBuffer is available.
* @function Engine.isSharedArrayBufferAvailable
*/
isSharedArrayBufferAvailable: function () {
return 'SharedArrayBuffer' in window;
},
/**
* Check whether the AudioContext supports AudioWorkletNodes.
*
* @returns {boolean} If AudioWorkletNode is available.
* @function Engine.isAudioWorkletAvailable
*/
isAudioWorkletAvailable: function () {
return 'AudioContext' in window && 'audioWorklet' in AudioContext.prototype;
},
/**
* Return an array of missing required features (as string).
*
* @returns {Array<string>} A list of human-readable missing features.
* @function Engine.getMissingFeatures
*/
getMissingFeatures: function () {
const missing = [];
if (!Features.isWebGLAvailable(2)) {
missing.push('WebGL2');
}
if (!Features.isFetchAvailable()) {
missing.push('Fetch');
}
if (!Features.isSecureContext()) {
missing.push('Secure Context');
}
if (!Features.isCrossOriginIsolated()) {
missing.push('Cross Origin Isolation');
}
if (!Features.isSharedArrayBufferAvailable()) {
missing.push('SharedArrayBuffer');
}
// Audio is normally optional since we have a dummy fallback.
return missing;
},
};

View file

@ -4,7 +4,7 @@
"version": "1.0.0",
"description": "Development and linting setup for Godot's Web platform code",
"scripts": {
"docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js --destination ''",
"docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js js/engine/features.js --destination ''",
"lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools",
"lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js",
"lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js",