diff --git a/web/extension/js/index.js b/web/extension/js/index.js index 56f21aba4..7c9ab9d9d 100644 --- a/web/extension/js/index.js +++ b/web/extension/js/index.js @@ -1,17 +1,12 @@ -import { construct_public_api } from "../../js-src/public-api"; +import { PublicAPI } from "../../js-src/public-api"; +import { SourceAPI } from "../../js-src/source-api"; import { get_config_options } from "../../js-src/config"; let html = document.getElementsByTagName("html")[0]; let page_options = get_config_options(html); if (!page_options.optout) { - window.RufflePlayer = window.RufflePlayer || {}; - window.RufflePlayer.extension = construct_public_api(); - - //TODO: proper version negotiation - if (window.RufflePlayer.init === undefined) { - window.RufflePlayer.init = window.RufflePlayer.extension.init; - } + window.RufflePlayer = PublicAPI.negotiate(window.RufflePlayer, "extension", new SourceAPI()); //This is intended for sites that don't configure Ruffle themselves. //If the page calls Ruffle before DOMContentLoaded, then we hold off on the diff --git a/web/js-src/public-api.js b/web/js-src/public-api.js index debf86512..d8b2f3739 100644 --- a/web/js-src/public-api.js +++ b/web/js-src/public-api.js @@ -1,14 +1,113 @@ -import { interdict } from "./interdiction"; - /** - * Construct a public API for this particular iteration of Ruffle for Web. + * Represents the Ruffle public API. + * + * The public API exists primarily to allow multiple installs of Ruffle on a + * page (e.g. an extension install and a local one) to cooperate. The first to + * load "wins" and installs it's Public API class, and then all other Ruffle + * sources install their sources into the Public API. + * + * As a result, this class's functionality needs to be backwards and forwards + * compatible, so it is very minimal. Any proposed change to the class must be + * tested against previous self-hosted versions of Ruffle. We also allow target + * pages to construct a minimally useful version of this class to declare + * configuration before Ruffle has actually had a chance to load. Thus, this + * class's constructor specifically allows a previous version of the Public API + * to be upgraded from. */ -export function construct_public_api() { - return { - "version": "0.1.0", - "init": function (interdictions) { - window.RufflePlayer.invoked = true; - interdict(interdictions); +export class PublicAPI { + /** + * Construct the Ruffle public API. + * + * Do not use this function to negotiate a public API. Instead, use + * `public_api` to install your Ruffle source into an existing public API + * if it exists. + * + * @param {object} prev What used to be in the public API slot. + * + * This is used to upgrade from a prior version of the public API, or from + * a user-defined configuration object placed in the public API slot. + */ + constructor(prev) { + this.sources = {}; + this.config = {}; + this.invoked = false; + + if (prev !== undefined) { + if (prev.constructor.name === PublicAPI.name) { + /// We're upgrading from a previous API to a new one. + this.sources = prev.sources; + this.config = prev.config; + this.invoked = prev.invoked; + this.conflict = prev.conflict; + } else if (prev.constructor === Object && prev.interdictions !== undefined) { + /// We're the first, install user configuration + this.config = prev; + } else { + /// We're the first, but conflicting with someone else. + this.conflict = prev; + } } + } + + /** + * Install a given source into the Ruffle Public API. + * + * @param {string} name The name of the source. + * @param {object} api The public API object. This must conform to the shape + * of `SourceAPI`. + */ + install_source(name, api) { + + } + + /** + * Negotiate and start Ruffle. + * + * @param {array} interdictions The list of interdictions to configure. + */ + init(interdictions) { + window.RufflePlayer.invoked = true; + + for (var key in this.sources) { + if (this.sources.hasOwnProperty(key)) { + return this.sources[key].init(interdictions) + } + } + } + + /** + * Join a source into the public API, if it doesn't already exist. + * + * @param {*} prev_ruffle The previous iteration of the Ruffle API. + * + * The `prev_ruffle` param lists the previous object in the RufflePlayer + * slot. We perform some checks to see if this is a Ruffle public API or a + * conflicting object. If this is conflicting, then a new public API will + * be constructed (see the constructor information for what happens to + * `prev_ruffle`). + * + * @param {string|undefined} source_name The name of this particular + * Ruffle source. + * + * @param {object|undefined} source_api The Ruffle source to add. + * + * If both parameters are provided they will be used to define a new Ruffle + * source to install into the public API. + * + * @returns {object} The Ruffle Public API. + */ + static negotiate(prev_ruffle, source_name, source_api) { + let public_api; + if (prev_ruffle !== undefined && prev_ruffle.constructor.name == PublicAPI.name) { + public_api = prev_ruffle; + } else { + public_api = new PublicAPI(prev_ruffle); + } + + if (source_name !== undefined && source_api !== undefined) { + public_api.install_source(source_name, source_api); + } + + return public_api; }; } \ No newline at end of file diff --git a/web/js-src/source-api.js b/web/js-src/source-api.js new file mode 100644 index 000000000..8369be10d --- /dev/null +++ b/web/js-src/source-api.js @@ -0,0 +1,19 @@ +import { interdict } from "./interdiction"; + +/** + * Represents this particular version of Ruffle. + * + * Multiple APIs can be instantiated from different sources; e.g. an "extension" + * version, versus a "local" version. This expresses to the Public API + * negotiator (see `PublicAPI`) what this particular version of Ruffle is and + * how to control it. + */ +export class SourceAPI { + get version() { + return "0.1.0"; + } + + init(interdictions) { + interdict(interdictions); + } +} \ No newline at end of file diff --git a/web/selfhosted/js/ruffle.js b/web/selfhosted/js/ruffle.js index b215e7ab7..526623124 100644 --- a/web/selfhosted/js/ruffle.js +++ b/web/selfhosted/js/ruffle.js @@ -1,22 +1,4 @@ -import { construct_public_api } from "../../js-src/public-api"; -import { get_config_options } from "../../js-src/config"; +import { PublicAPI } from "../../js-src/public-api"; +import { SourceAPI } from "../../js-src/source-api"; -let html = document.getElementsByTagName("html")[0]; -let page_options = get_config_options(html); - -window.RufflePlayer = window.RufflePlayer || {}; -window.RufflePlayer.local = construct_public_api(); - -//TODO: proper version negotiation -if (window.RufflePlayer.init === undefined) { - window.RufflePlayer.init = window.RufflePlayer.local.init; -} - -//This is intended for sites that don't configure Ruffle themselves. -//If the page calls Ruffle before DOMContentLoaded, then we hold off on the -//standard set of interdictions. -window.addEventListener("DOMContentLoaded", function () { - if (!window.RufflePlayer.invoked) { - window.RufflePlayer.init(["plugin-detect", "static-content"]); - } -}) \ No newline at end of file +window.RufflePlayer = PublicAPI.negotiate(window.RufflePlayer, "local", new SourceAPI()); \ No newline at end of file