ruffle/web/packages/core/src/polyfills.js

205 lines
6.9 KiB
JavaScript
Raw Normal View History

const RuffleObject = require("./ruffle-object");
const RuffleEmbed = require("./ruffle-embed");
const { install_plugin, FLASH_PLUGIN } = require("./plugin-polyfill");
const { public_path } = require("./public-path");
2019-08-22 01:02:43 +00:00
2020-04-02 14:08:33 +00:00
if (!window.RufflePlayer) {
window.RufflePlayer = {};
2020-04-02 14:08:33 +00:00
}
let top_level_ruffle_config;
let ruffle_script_src = public_path({}, "ruffle.js");
2020-04-02 14:08:33 +00:00
if (window.RufflePlayer.config) {
top_level_ruffle_config = window.RufflePlayer.config;
ruffle_script_src = public_path(window.RufflePlayer.config, "ruffle.js");
2020-04-02 14:08:33 +00:00
}
/* public_path returns the directory where the file is, *
* so we need to append the filename. We don't need to *
* worry about the directory not having a slash because *
* public_path appends a slash. */
ruffle_script_src += "ruffle.js";
2019-08-22 01:02:43 +00:00
/**
2019-10-16 02:02:03 +00:00
* Polyfill native elements with Ruffle equivalents.
*
2019-10-16 02:02:03 +00:00
* This polyfill isn't fool-proof: If there's a chance site JavaScript has
* access to a pre-polyfill element, then this will break horribly. We can
2019-08-22 01:02:43 +00:00
* keep native objects out of the DOM, and thus out of JavaScript's grubby
* little hands, but only if we load first.
*/
2020-04-02 14:08:33 +00:00
let objects;
let embeds;
function replace_flash_instances() {
try {
// Create live collections to track embed tags.
objects = objects || document.getElementsByTagName("object");
embeds = embeds || document.getElementsByTagName("embed");
2019-08-22 01:02:43 +00:00
// Replace <object> first, because <object> often wraps <embed>.
for (let elem of Array.from(objects)) {
if (RuffleObject.is_interdictable(elem)) {
let ruffle_obj = RuffleObject.from_native_object_element(elem);
elem.replaceWith(ruffle_obj);
}
}
for (let elem of Array.from(embeds)) {
if (RuffleEmbed.is_interdictable(elem)) {
let ruffle_obj = RuffleEmbed.from_native_embed_element(elem);
elem.replaceWith(ruffle_obj);
}
2019-08-22 01:02:43 +00:00
}
} catch (err) {
console.error(
"Serious error encountered when polyfilling native Flash elements: " +
err
);
2019-08-22 01:02:43 +00:00
}
}
2019-10-16 02:02:03 +00:00
function polyfill_static_content() {
replace_flash_instances();
}
2019-08-22 01:02:43 +00:00
2019-10-16 02:02:03 +00:00
function polyfill_dynamic_content() {
// Listen for changes to the DOM. If nodes are added, re-check for any Flash instances.
const observer = new MutationObserver(function (mutationsList) {
// If any nodes were added, re-run the polyfill to replace any new instances.
let nodesAdded = mutationsList.some(
(mutation) => mutation.addedNodes.length > 0
);
if (nodesAdded) {
replace_flash_instances();
2019-08-22 01:02:43 +00:00
}
});
2019-08-22 01:02:43 +00:00
observer.observe(document, { childList: true, subtree: true });
}
function falsify_plugin_detection() {
install_plugin(FLASH_PLUGIN);
}
2020-04-02 14:08:33 +00:00
function load_ruffle_player_into_frame(event) {
2020-05-23 17:26:40 +00:00
loadFrame(event.currentTarget.contentWindow);
}
function loadFrame(current_frame) {
2020-04-02 14:08:33 +00:00
let frame_document;
try {
frame_document = current_frame.document;
2020-04-02 14:08:33 +00:00
if (!frame_document) {
console.log("Frame has no document.");
return;
}
} catch (e) {
2020-04-02 14:08:33 +00:00
console.log("Error Getting Frame: " + e.message);
return;
}
if (!current_frame.RufflePlayer) {
2020-04-02 14:08:33 +00:00
/* Make sure we populate the frame's window.RufflePlayer.config */
current_frame.RufflePlayer = {};
current_frame.RufflePlayer.config = top_level_ruffle_config;
2020-04-02 14:08:33 +00:00
let script = frame_document.createElement("script");
script.src = ruffle_script_src; /* Load this script(ruffle.js) into the frame */
2020-04-02 14:08:33 +00:00
frame_document.body.appendChild(script);
} else {
2020-04-02 14:08:33 +00:00
console.log("(i)frame already has RufflePlayer");
}
polyfill_frames_common(current_frame);
}
2020-05-23 17:26:40 +00:00
function handleFrames(frameList) {
let originalOnLoad;
for (let i = 0; i < frameList.length; i++) {
let current_frame = frameList[i];
/* Apparently, using addEventListener attaches the event *
2020-04-02 14:08:33 +00:00
* to the dummy document, which is overwritten when the *
* iframe is loaded, so we do this. It can only works if *
* it's attached to the frame object itself, which is why *
* we're using *
* depth.document.getElementsByTagName("iframe") instead *
* of depth.frames to get the iframes at the depth. *
* Also, this way we should be able to handle frame *
* frame navigation, which is good. */
2020-05-23 17:26:40 +00:00
setTimeout(function () {
if (
current_frame.contentDocument.readyState &&
current_frame.contentDocument.readyState == "complete"
) {
loadFrame(current_frame.contentWindow);
}
}, 500);
if ((originalOnLoad = current_frame.onload)) {
current_frame.onload = function (event) {
originalOnLoad(event);
load_ruffle_player_into_frame(event);
};
} else {
current_frame.onload = load_ruffle_player_into_frame;
}
2020-04-02 14:08:33 +00:00
polyfill_frames_common(current_frame.contentWindow);
}
}
2020-05-23 17:26:40 +00:00
function polyfill_frames_common(depth) {
handleFrames(depth.document.getElementsByTagName("iframe"));
handleFrames(depth.document.getElementsByTagName("frame"));
}
2020-04-02 14:08:33 +00:00
function polyfill_static_frames() {
polyfill_frames_common(window);
}
function ruffle_frame_listener(mutationsList) {
2020-04-02 14:08:33 +00:00
/* Basically the same as the listener for dynamic embeds. */
let nodesAdded = mutationsList.some(
(mutation) => mutation.addedNodes.length > 0
);
2020-04-02 14:08:33 +00:00
if (nodesAdded) {
polyfill_frames_common(window);
}
}
function polyfill_dynamic_frames() {
const observer = new MutationObserver(ruffle_frame_listener);
observer.observe(document, { childList: true, subtree: true });
2020-04-02 14:08:33 +00:00
}
function polyfill_frames() {
polyfill_static_frames();
if (running_polyfills.indexOf("dynamic-content") != -1) {
polyfill_dynamic_frames();
}
}
let running_polyfills = [];
let polyfills = {
2019-10-16 02:02:03 +00:00
"static-content": polyfill_static_content,
"dynamic-content": polyfill_dynamic_content,
2020-04-02 14:08:33 +00:00
"plugin-detect": falsify_plugin_detection,
frames: polyfill_frames,
};
exports.polyfill = function polyfill(polyfill_list) {
2020-04-02 14:08:33 +00:00
for (let i = 0; i < polyfill_list.length; i += 1) {
2019-10-16 02:02:03 +00:00
if (running_polyfills.indexOf(polyfill_list[i]) !== -1) {
continue;
}
if (
!Object.prototype.hasOwnProperty.call(polyfills, polyfill_list[i])
) {
throw new Error(
"Requested nonexistent polyfill: " + polyfill_list[i]
);
}
2019-10-16 02:02:03 +00:00
running_polyfills.push(polyfill_list[i]);
2020-04-02 14:08:33 +00:00
let this_polyfill = polyfills[polyfill_list[i]];
2019-10-15 02:52:20 +00:00
2019-10-16 02:02:03 +00:00
if (this_polyfill.dependencies !== undefined) {
polyfill(this_polyfill.dependencies);
}
2019-10-16 02:02:03 +00:00
this_polyfill();
}
};