203 lines
8.6 KiB
JavaScript
203 lines
8.6 KiB
JavaScript
import {Config} from "data";
|
|
import Logger from "common/logger";
|
|
import AddonManager from "./addonmanager";
|
|
import AddonError from "../structs/addonerror";
|
|
import Settings from "./settingsmanager";
|
|
import Strings from "./strings";
|
|
import IPC from "./ipc";
|
|
import Events from "./emitter";
|
|
|
|
import Toasts from "../ui/toasts";
|
|
import Modals from "../ui/modals";
|
|
import SettingsRenderer from "../ui/settings";
|
|
|
|
const path = require("path");
|
|
const electron = require("electron");
|
|
const vm = require("vm");
|
|
// const electronRemote = require("electron").remote;
|
|
|
|
// window.$ = window.jQuery = function() {}
|
|
|
|
export default new class PluginManager extends AddonManager {
|
|
get name() {return "PluginManager";}
|
|
get moduleExtension() {return ".js";}
|
|
get extension() {return ".plugin.js";}
|
|
get duplicatePattern() {return /\.plugin\([0-9]+\)\.js/;}
|
|
get addonFolder() {return path.resolve(Config.dataPath, "plugins");}
|
|
get prefix() {return "plugin";}
|
|
get language() {return "javascript";}
|
|
|
|
constructor() {
|
|
super();
|
|
this.promises = {};
|
|
this.onSwitch = this.onSwitch.bind(this);
|
|
this.observer = new MutationObserver((mutations) => {
|
|
for (let i = 0, mlen = mutations.length; i < mlen; i++) {
|
|
this.onMutation(mutations[i]);
|
|
}
|
|
});
|
|
}
|
|
|
|
async initialize() {
|
|
const errors = await super.initialize();
|
|
this.setupFunctions();
|
|
Settings.registerPanel("plugins", Strings.Panels.plugins, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.plugins, this.addonList, this.state, {
|
|
type: this.prefix,
|
|
folder: this.addonFolder,
|
|
onChange: this.togglePlugin.bind(this),
|
|
reload: this.reloadPlugin.bind(this),
|
|
refreshList: this.updatePluginList.bind(this),
|
|
saveAddon: this.saveAddon.bind(this),
|
|
editAddon: this.editAddon.bind(this),
|
|
deleteAddon: this.deleteAddon.bind(this),
|
|
prefix: this.prefix
|
|
})});
|
|
return errors;
|
|
}
|
|
|
|
/* Aliases */
|
|
updatePluginList() {return this.updateList();}
|
|
loadAllPlugins() {return this.loadAllAddons();}
|
|
|
|
enablePlugin(idOrAddon) {return this.enableAddon(idOrAddon);}
|
|
disablePlugin(idOrAddon) {return this.disableAddon(idOrAddon);}
|
|
togglePlugin(id) {return this.toggleAddon(id);}
|
|
|
|
unloadPlugin(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);}
|
|
loadPlugin(filename) {return this.loadAddon(filename);}
|
|
|
|
async loadAddon(filename) {
|
|
const error = await super.loadAddon(filename);
|
|
if (error) Modals.showAddonErrors({plugins: [error]});
|
|
}
|
|
|
|
async reloadPlugin(idOrFileOrAddon) {
|
|
const error = await this.reloadAddon(idOrFileOrAddon);
|
|
if (error) Modals.showAddonErrors({plugins: [error]});
|
|
return typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
|
|
}
|
|
|
|
/* Overrides */
|
|
initializeAddon(addon) {
|
|
if (!addon.exports) return new AddonError(addon.name, addon.filename, "Plugin had no exports", {message: "Plugin had no exports or no name property.", stack: ""});
|
|
try {
|
|
const PluginClass = addon.exports;
|
|
const thePlugin = new PluginClass();
|
|
addon.instance = thePlugin;
|
|
addon.name = thePlugin.getName ? thePlugin.getName() : addon.name || "No name";
|
|
addon.author = thePlugin.getAuthor ? thePlugin.getAuthor() : addon.author || "No author";
|
|
addon.description = thePlugin.getDescription ? thePlugin.getDescription() : addon.description || "No description";
|
|
addon.version = thePlugin.getVersion ? thePlugin.getVersion() : addon.version || "No version";
|
|
try {
|
|
if (typeof(addon.instance.load) == "function") addon.instance.load();
|
|
}
|
|
catch (error) {
|
|
this.state[addon.id] = false;
|
|
return new AddonError(addon.name, addon.filename, "load() could not be fired.", {message: error.message, stack: error.stack});
|
|
}
|
|
}
|
|
catch (error) {return new AddonError(addon.name, addon.filename, "Could not be constructed.", {message: error.message, stack: error.stack});}
|
|
}
|
|
|
|
getFileModification(module, fileContent, meta) {
|
|
fileContent += `\nif (module.exports.default) {module.exports = module.exports.default;}\nif (!module.exports.prototype || !module.exports.prototype.start) {module.exports = ${meta.exports || meta.name};}`;
|
|
|
|
window.global = window;
|
|
window.module = module;
|
|
window.__filename = path.basename(module.filename);
|
|
window.__dirname = this.addonFolder;
|
|
const wrapped = `(${vm.compileFunction(fileContent, ["exports", "require", "module", "__filename", "__dirname"]).toString()})`;
|
|
// console.log(module);
|
|
module.exports = new Promise(resolve => {
|
|
IPC.runScript(`${wrapped}(window.module.exports, window.require, window.module, window.__filename, window.__dirname)`).then(() => {
|
|
// console.log(window.module);
|
|
meta.exports = module.exports;
|
|
module.exports = meta;
|
|
delete window.module;
|
|
delete window.__filename;
|
|
delete window.__dirname;
|
|
resolve();
|
|
});
|
|
});
|
|
// module._compile(fileContent, module.filename);
|
|
// meta.exports = module.exports;
|
|
// module.exports = meta;
|
|
return "";
|
|
}
|
|
|
|
startAddon(id) {return this.startPlugin(id);}
|
|
stopAddon(id) {return this.stopPlugin(id);}
|
|
getAddon(id) {return this.getPlugin(id);}
|
|
|
|
startPlugin(idOrAddon) {
|
|
const addon = typeof(idOrAddon) == "string" ? this.addonList.find(p => p.id == idOrAddon) : idOrAddon;
|
|
if (!addon) return;
|
|
const plugin = addon.instance;
|
|
try {
|
|
plugin.start();
|
|
}
|
|
catch (err) {
|
|
this.state[addon.id] = false;
|
|
Toasts.error(Strings.Addons.couldNotStart.format({name: addon.name, version: addon.version}));
|
|
Logger.stacktrace(this.name, addon.name + " could not be started.", err);
|
|
return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "start()"}), {message: err.message, stack: err.stack});
|
|
}
|
|
this.emit("started", addon.id);
|
|
Toasts.show(Strings.Addons.enabled.format({name: addon.name, version: addon.version}));
|
|
}
|
|
|
|
stopPlugin(idOrAddon) {
|
|
const addon = typeof(idOrAddon) == "string" ? this.addonList.find(p => p.id == idOrAddon) : idOrAddon;
|
|
if (!addon) return;
|
|
const plugin = addon.instance;
|
|
try {
|
|
plugin.stop();
|
|
}
|
|
catch (err) {
|
|
this.state[addon.id] = false;
|
|
Toasts.error(Strings.Addons.couldNotStop.format({name: addon.name, version: addon.version}));
|
|
Logger.stacktrace(this.name, addon.name + " could not be stopped.", err);
|
|
return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "stop()"}), {message: err.message, stack: err.stack});
|
|
}
|
|
this.emit("stopped", addon.id);
|
|
Toasts.show(Strings.Addons.disabled.format({name: addon.name, version: addon.version}));
|
|
}
|
|
|
|
getPlugin(idOrFile) {
|
|
const addon = this.addonList.find(c => c.id == idOrFile || c.filename == idOrFile);
|
|
if (!addon) return;
|
|
return addon;
|
|
}
|
|
|
|
setupFunctions() {
|
|
// electronRemote.getCurrentWebContents().on("did-navigate-in-page", this.onSwitch.bind(this));
|
|
Events.on("navigate", this.onSwitch);
|
|
// ipc.on(IPCEvents.NAVIGATE, this.onSwitch);
|
|
this.observer.observe(document, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
}
|
|
|
|
onSwitch() {
|
|
for (let i = 0; i < this.addonList.length; i++) {
|
|
const plugin = this.addonList[i].instance;
|
|
if (!this.state[this.addonList[i].id]) continue;
|
|
if (typeof(plugin.onSwitch) === "function") {
|
|
try {plugin.onSwitch();}
|
|
catch (err) {Logger.stacktrace(this.name, "Unable to fire onSwitch for " + this.addonList[i].name + ".", err);}
|
|
}
|
|
}
|
|
}
|
|
|
|
onMutation(mutation) {
|
|
for (let i = 0; i < this.addonList.length; i++) {
|
|
const plugin = this.addonList[i].instance;
|
|
if (!this.state[this.addonList[i].id]) continue;
|
|
if (typeof plugin.observer === "function") {
|
|
try {plugin.observer(mutation);}
|
|
catch (err) {Logger.stacktrace(this.name, "Unable to fire observer for " + this.addonList[i].name + ".", err);}
|
|
}
|
|
}
|
|
}
|
|
}; |