2019-06-08 08:35:43 +02:00
import { Config } from "data" ;
2021-03-06 09:30:16 +01:00
import Logger from "common/logger" ;
2019-06-27 22:18:40 +02:00
import AddonManager from "./addonmanager" ;
import AddonError from "../structs/addonerror" ;
2019-06-08 08:35:43 +02:00
import Settings from "./settingsmanager" ;
2019-06-25 22:36:34 +02:00
import Strings from "./strings" ;
2021-03-06 09:30:16 +01:00
import IPC from "./ipc" ;
import Events from "./emitter" ;
2019-06-23 06:11:50 +02:00
import Toasts from "../ui/toasts" ;
import Modals from "../ui/modals" ;
import SettingsRenderer from "../ui/settings" ;
2019-06-08 08:35:43 +02:00
const path = require ( "path" ) ;
2021-03-01 01:19:25 +01:00
const electron = require ( "electron" ) ;
const vm = require ( "vm" ) ;
// const electronRemote = require("electron").remote;
// window.$ = window.jQuery = function() {}
2019-06-08 08:35:43 +02:00
2019-06-27 22:18:40 +02:00
export default new class PluginManager extends AddonManager {
2019-06-08 08:35:43 +02:00
get name ( ) { return "PluginManager" ; }
get moduleExtension ( ) { return ".js" ; }
get extension ( ) { return ".plugin.js" ; }
2021-04-05 07:43:30 +02:00
get duplicatePattern ( ) { return /\.plugin\s?\([0-9]+\)\.js/ ; }
2019-06-27 22:18:40 +02:00
get addonFolder ( ) { return path . resolve ( Config . dataPath , "plugins" ) ; }
2019-06-08 08:35:43 +02:00
get prefix ( ) { return "plugin" ; }
2019-06-30 07:32:14 +02:00
get language ( ) { return "javascript" ; }
2019-06-08 08:35:43 +02:00
constructor ( ) {
super ( ) ;
2021-03-01 01:19:25 +01:00
this . promises = { } ;
2019-06-08 08:35:43 +02:00
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 ] ) ;
}
} ) ;
}
2019-05-28 20:19:48 +02:00
2021-03-18 22:50:47 +01:00
initialize ( ) {
const errors = super . initialize ( ) ;
2019-06-24 21:47:24 +02:00
this . setupFunctions ( ) ;
2019-06-27 22:18:40 +02:00
Settings . registerPanel ( "plugins" , Strings . Panels . plugins , { element : ( ) => SettingsRenderer . getAddonPanel ( Strings . Panels . plugins , this . addonList , this . state , {
2020-11-07 07:03:29 +01:00
type : this . prefix ,
2019-06-27 22:18:40 +02:00
folder : this . addonFolder ,
2019-06-24 21:47:24 +02:00
onChange : this . togglePlugin . bind ( this ) ,
reload : this . reloadPlugin . bind ( this ) ,
2019-06-30 07:32:14 +02:00
refreshList : this . updatePluginList . bind ( this ) ,
saveAddon : this . saveAddon . bind ( this ) ,
editAddon : this . editAddon . bind ( this ) ,
deleteAddon : this . deleteAddon . bind ( this ) ,
prefix : this . prefix
2019-06-24 21:47:24 +02:00
} ) } ) ;
return errors ;
}
2019-06-08 08:35:43 +02:00
/* Aliases */
updatePluginList ( ) { return this . updateList ( ) ; }
2019-06-27 22:18:40 +02:00
loadAllPlugins ( ) { return this . loadAllAddons ( ) ; }
2019-05-28 20:19:48 +02:00
2019-06-27 22:18:40 +02:00
enablePlugin ( idOrAddon ) { return this . enableAddon ( idOrAddon ) ; }
disablePlugin ( idOrAddon ) { return this . disableAddon ( idOrAddon ) ; }
togglePlugin ( id ) { return this . toggleAddon ( id ) ; }
2019-05-28 20:19:48 +02:00
2019-06-27 22:18:40 +02:00
unloadPlugin ( idOrFileOrAddon ) { return this . unloadAddon ( idOrFileOrAddon ) ; }
2020-10-06 23:44:10 +02:00
loadPlugin ( filename ) { return this . loadAddon ( filename ) ; }
2019-05-28 20:19:48 +02:00
2021-04-06 20:09:43 +02:00
loadAddon ( filename , shouldCTE = true ) {
2021-03-18 22:50:47 +01:00
const error = super . loadAddon ( filename ) ;
2021-04-06 20:09:43 +02:00
if ( error && shouldCTE ) Modals . showAddonErrors ( { plugins : [ error ] } ) ;
return error ;
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
2021-03-18 22:50:47 +01:00
reloadPlugin ( idOrFileOrAddon ) {
const error = this . reloadAddon ( idOrFileOrAddon ) ;
2019-06-27 22:18:40 +02:00
if ( error ) Modals . showAddonErrors ( { plugins : [ error ] } ) ;
return typeof ( idOrFileOrAddon ) == "string" ? this . addonList . find ( c => c . id == idOrFileOrAddon || c . filename == idOrFileOrAddon ) : idOrFileOrAddon ;
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
/* Overrides */
2019-06-27 22:18:40 +02:00
initializeAddon ( addon ) {
2021-04-06 20:09:43 +02:00
if ( ! addon . exports ) return new AddonError ( addon . name , addon . filename , "Plugin had no exports" , { message : "Plugin had no exports or no name property." , stack : "" } , this . prefix ) ;
2019-06-08 08:35:43 +02:00
try {
2020-07-29 21:06:54 +02:00
const PluginClass = addon . exports ;
2020-07-25 10:22:57 +02:00
const thePlugin = new PluginClass ( ) ;
2020-07-29 21:06:54 +02:00
addon . instance = thePlugin ;
2020-11-07 07:03:29 +01:00
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" ;
2019-05-28 20:19:48 +02:00
try {
2020-07-29 21:06:54 +02:00
if ( typeof ( addon . instance . load ) == "function" ) addon . instance . load ( ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
catch ( error ) {
2019-06-27 22:18:40 +02:00
this . state [ addon . id ] = false ;
2021-04-06 20:09:43 +02:00
return new AddonError ( addon . name , addon . filename , "load() could not be fired." , { message : error . message , stack : error . stack } , this . prefix ) ;
2019-05-28 20:19:48 +02:00
}
}
2021-04-06 20:09:43 +02:00
catch ( error ) { return new AddonError ( addon . name , addon . filename , "Could not be constructed." , { message : error . message , stack : error . stack } , this . prefix ) ; }
2019-05-28 20:19:48 +02:00
}
2019-06-27 22:18:40 +02:00
getFileModification ( module , fileContent , meta ) {
2020-10-20 20:38:40 +02:00
fileContent += ` \n if (module.exports.default) {module.exports = module.exports.default;} \n if (!module.exports.prototype || !module.exports.prototype.start) {module.exports = ${ meta . exports || meta . name } ;} ` ;
2021-03-01 01:19:25 +01:00
window . global = window ;
window . module = module ;
window . _ _filename = path . basename ( module . filename ) ;
window . _ _dirname = this . addonFolder ;
2021-03-06 09:30:16 +01:00
const wrapped = ` ( ${ vm . compileFunction ( fileContent , [ "exports" , "require" , "module" , "__filename" , "__dirname" ] ) . toString ( ) } ) ` ;
2021-03-18 22:50:47 +01:00
const final = ` ${ wrapped } (window.module.exports, window.require, window.module, window.__filename, window.__dirname) ` ;
const container = document . createElement ( "script" ) ;
container . innerHTML = final ;
container . id = ` ${ meta . id } -script-container ` ;
// container.src = `data:text/javascript;${btoa(final)}`;
document . head . append ( container ) ;
meta . exports = module . exports ;
module . exports = meta ;
delete window . module ;
delete window . _ _filename ;
delete window . _ _dirname ;
2020-07-29 21:06:54 +02:00
return "" ;
2019-05-28 20:19:48 +02:00
}
2019-06-27 22:18:40 +02:00
startAddon ( id ) { return this . startPlugin ( id ) ; }
stopAddon ( id ) { return this . stopPlugin ( id ) ; }
2020-07-19 01:01:49 +02:00
getAddon ( id ) { return this . getPlugin ( id ) ; }
2019-06-08 08:35:43 +02:00
2019-06-27 22:18:40 +02:00
startPlugin ( idOrAddon ) {
const addon = typeof ( idOrAddon ) == "string" ? this . addonList . find ( p => p . id == idOrAddon ) : idOrAddon ;
if ( ! addon ) return ;
2020-07-29 21:06:54 +02:00
const plugin = addon . instance ;
2019-06-08 08:35:43 +02:00
try {
plugin . start ( ) ;
}
catch ( err ) {
2019-06-27 22:18:40 +02:00
this . state [ addon . id ] = false ;
2020-07-19 01:01:49 +02:00
Toasts . error ( Strings . Addons . couldNotStart . format ( { name : addon . name , version : addon . version } ) ) ;
2021-03-18 22:50:47 +01:00
Logger . stacktrace ( this . name , ` ${ addon . name } v ${ addon . version } could not be started. ` , err ) ;
2021-04-06 20:09:43 +02:00
return new AddonError ( addon . name , addon . filename , Strings . Addons . enabled . format ( { method : "start()" } ) , { message : err . message , stack : err . stack } , this . prefix ) ;
2019-06-08 08:35:43 +02:00
}
2020-07-19 01:01:49 +02:00
this . emit ( "started" , addon . id ) ;
Toasts . show ( Strings . Addons . enabled . format ( { name : addon . name , version : addon . version } ) ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
2019-06-27 22:18:40 +02:00
stopPlugin ( idOrAddon ) {
const addon = typeof ( idOrAddon ) == "string" ? this . addonList . find ( p => p . id == idOrAddon ) : idOrAddon ;
if ( ! addon ) return ;
2020-07-29 21:06:54 +02:00
const plugin = addon . instance ;
2019-06-08 08:35:43 +02:00
try {
plugin . stop ( ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
catch ( err ) {
2019-06-27 22:18:40 +02:00
this . state [ addon . id ] = false ;
2020-07-19 01:01:49 +02:00
Toasts . error ( Strings . Addons . couldNotStop . format ( { name : addon . name , version : addon . version } ) ) ;
2021-03-18 22:50:47 +01:00
Logger . stacktrace ( this . name , ` ${ addon . name } v ${ addon . version } could not be started. ` , err ) ;
2021-04-06 20:09:43 +02:00
return new AddonError ( addon . name , addon . filename , Strings . Addons . enabled . format ( { method : "stop()" } ) , { message : err . message , stack : err . stack } , this . prefix ) ;
2019-06-08 08:35:43 +02:00
}
2020-07-19 01:01:49 +02:00
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 ;
2021-02-22 23:53:21 +01:00
return addon ;
2019-06-08 08:35:43 +02:00
}
setupFunctions ( ) {
2021-03-01 01:19:25 +01:00
// electronRemote.getCurrentWebContents().on("did-navigate-in-page", this.onSwitch.bind(this));
2021-03-06 09:30:16 +01:00
Events . on ( "navigate" , this . onSwitch ) ;
// ipc.on(IPCEvents.NAVIGATE, this.onSwitch);
2019-06-08 08:35:43 +02:00
this . observer . observe ( document , {
childList : true ,
subtree : true
} ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
onSwitch ( ) {
2019-06-27 22:18:40 +02:00
for ( let i = 0 ; i < this . addonList . length ; i ++ ) {
2020-07-29 21:06:54 +02:00
const plugin = this . addonList [ i ] . instance ;
2019-06-27 22:18:40 +02:00
if ( ! this . state [ this . addonList [ i ] . id ] ) continue ;
2019-06-08 08:35:43 +02:00
if ( typeof ( plugin . onSwitch ) === "function" ) {
2020-07-25 10:22:57 +02:00
try { plugin . onSwitch ( ) ; }
2021-03-18 22:50:47 +01:00
catch ( err ) { Logger . stacktrace ( this . name , ` Unable to fire onSwitch for ${ this . addonList [ i ] . name } v ${ this . addonList [ i ] . version } ` , err ) ; }
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
onMutation ( mutation ) {
2019-06-27 22:18:40 +02:00
for ( let i = 0 ; i < this . addonList . length ; i ++ ) {
2020-07-29 21:06:54 +02:00
const plugin = this . addonList [ i ] . instance ;
2019-06-27 22:18:40 +02:00
if ( ! this . state [ this . addonList [ i ] . id ] ) continue ;
2019-06-08 08:35:43 +02:00
if ( typeof plugin . observer === "function" ) {
2020-07-25 10:22:57 +02:00
try { plugin . observer ( mutation ) ; }
2021-03-18 22:50:47 +01:00
catch ( err ) { Logger . stacktrace ( this . name , ` Unable to fire observer for ${ this . addonList [ i ] . name } v ${ this . addonList [ i ] . version } ` , err ) ; }
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
} ;