Store patched functions in a weakmap, move events wrapper and prevent plugins from modifying the plugin and content prototypes and their internals object
This commit is contained in:
parent
e7b0acb5a0
commit
bc1f93dd89
|
@ -15,7 +15,17 @@ import Database from './database';
|
|||
export default class Content {
|
||||
|
||||
constructor(internals) {
|
||||
this.__internals = internals;
|
||||
Object.freeze(internals);
|
||||
Utils.deepfreeze(internals.info);
|
||||
Object.freeze(internals.paths);
|
||||
Object.freeze(internals.configs);
|
||||
|
||||
Object.defineProperty(this, '__internals', {
|
||||
value: internals,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false
|
||||
});
|
||||
|
||||
this.settings.on('setting-updated', event => this.events.emit('setting-updated', event));
|
||||
this.settings.on('settings-updated', event => this.events.emit('settings-updated', event));
|
||||
|
@ -170,3 +180,5 @@ export default class Content {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
Object.freeze(Content.prototype);
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* BetterDiscord Events Wrapper Module
|
||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const eventemitters = new WeakMap();
|
||||
|
||||
export default class EventsWrapper {
|
||||
constructor(eventemitter) {
|
||||
eventemitters.set(this, eventemitter);
|
||||
}
|
||||
|
||||
get eventSubs() {
|
||||
return this._eventSubs || (this._eventSubs = []);
|
||||
}
|
||||
|
||||
subscribe(event, callback) {
|
||||
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||
this.eventSubs.push({
|
||||
event,
|
||||
callback
|
||||
});
|
||||
eventemitters.get(this).on(event, callback);
|
||||
}
|
||||
|
||||
unsubscribe(event, callback) {
|
||||
for (let index of this.eventSubs) {
|
||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) return;
|
||||
eventemitters.get(this).off(event, this.eventSubs[index].callback);
|
||||
this.eventSubs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
for (let event of this.eventSubs) {
|
||||
eventemitters.get(this).off(event.event, event.callback);
|
||||
}
|
||||
this.eventSubs.splice(0, this.eventSubs.length);
|
||||
}
|
||||
}
|
|
@ -13,3 +13,4 @@ export { default as SocketProxy } from './socketproxy';
|
|||
export { default as EventHook } from './eventhook';
|
||||
export { default as Permissions } from './permissionmanager';
|
||||
export { default as Database } from './database';
|
||||
export { default as EventsWrapper } from './eventswrapper';
|
||||
|
|
|
@ -11,27 +11,27 @@
|
|||
const PermissionMap = {
|
||||
IDENTIFY: {
|
||||
HEADER: 'Access your account information',
|
||||
BODY: 'Allows :NAME: to read your account information(excluding user token)'
|
||||
BODY: 'Allows :NAME: to read your account information (excluding user token).'
|
||||
},
|
||||
READ_MESSAGES: {
|
||||
HEADER: 'Read all messages',
|
||||
BODY: 'Allows :NAME: to read all messages accessible through your Discord account'
|
||||
BODY: 'Allows :NAME: to read all messages accessible through your Discord account.'
|
||||
},
|
||||
SEND_MESSAGES: {
|
||||
HEADER: 'Send messages',
|
||||
BODY: 'Allows :NAME: to send messages on your behalf'
|
||||
BODY: 'Allows :NAME: to send messages on your behalf.'
|
||||
},
|
||||
DELETE_MESSAGES: {
|
||||
HEADER: 'Delete messages',
|
||||
BODY: 'Allows :NAME: to delete messages on your behalf'
|
||||
BODY: 'Allows :NAME: to delete messages on your behalf.'
|
||||
},
|
||||
EDIT_MESSAGES: {
|
||||
HEADER: 'Edit messages',
|
||||
BODY: 'Allows :NAME: to edit messages on your behalf'
|
||||
BODY: 'Allows :NAME: to edit messages on your behalf.'
|
||||
},
|
||||
JOIN_SERVERS: {
|
||||
HEADER: 'Join servers for you',
|
||||
BODY: 'Allows :NAME: to join servers on your behalf'
|
||||
BODY: 'Allows :NAME: to join servers on your behalf.'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,50 +14,20 @@ import ExtModuleManager from './extmodulemanager';
|
|||
import PluginManager from './pluginmanager';
|
||||
import ThemeManager from './thememanager';
|
||||
import Events from './events';
|
||||
import EventsWrapper from './eventswrapper';
|
||||
import WebpackModules from './webpackmodules';
|
||||
import { SettingsSet, SettingsCategory, Setting, SettingsScheme } from 'structs';
|
||||
import { BdMenuItems, Modals, DOM } from 'ui';
|
||||
import SettingsModal from '../ui/components/bd/modals/SettingsModal.vue';
|
||||
|
||||
class EventsWrapper {
|
||||
constructor(eventemitter) {
|
||||
this.__eventemitter = eventemitter;
|
||||
}
|
||||
|
||||
get eventSubs() {
|
||||
return this._eventSubs || (this._eventSubs = []);
|
||||
}
|
||||
|
||||
subscribe(event, callback) {
|
||||
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||
this.eventSubs.push({
|
||||
event,
|
||||
callback
|
||||
});
|
||||
this.__eventemitter.on(event, callback);
|
||||
}
|
||||
|
||||
unsubscribe(event, callback) {
|
||||
for (let index of this.eventSubs) {
|
||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) return;
|
||||
this.__eventemitter.off(event, this.eventSubs[index].callback);
|
||||
this.eventSubs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
for (let event of this.eventSubs) {
|
||||
this.__eventemitter.off(event.event, event.callback);
|
||||
}
|
||||
this._eventSubs = [];
|
||||
}
|
||||
}
|
||||
|
||||
export default class PluginApi {
|
||||
|
||||
constructor(pluginInfo) {
|
||||
this.pluginInfo = pluginInfo;
|
||||
this.Events = new EventsWrapper(Events);
|
||||
this._menuItems = undefined;
|
||||
this._injectedStyles = undefined;
|
||||
this._modalStack = undefined;
|
||||
}
|
||||
|
||||
get plugin() {
|
||||
|
@ -292,17 +262,20 @@ export default class PluginApi {
|
|||
return this.addModal(Modals.settings(settingsset, headertext, options));
|
||||
}
|
||||
get Modals() {
|
||||
return Object.defineProperty(Object.defineProperty({
|
||||
return Object.defineProperties({
|
||||
add: this.addModal.bind(this),
|
||||
close: this.closeModal.bind(this),
|
||||
closeAll: this.closeAllModals.bind(this),
|
||||
closeLast: this.closeLastModal.bind(this),
|
||||
basic: this.basicModal.bind(this),
|
||||
settings: this.settingsModal.bind(this)
|
||||
}, 'stack', {
|
||||
get: () => this.modalStack
|
||||
}), 'baseComponent', {
|
||||
get: () => this.baseModalComponent
|
||||
}, {
|
||||
stack: {
|
||||
get: () => this.modalStack
|
||||
},
|
||||
baseComponent: {
|
||||
get: () => this.baseModalComponent
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -396,3 +369,8 @@ export default class PluginApi {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// Stop plugins from modifying the plugin API for all plugins
|
||||
// Plugins can still modify their own plugin API object
|
||||
Object.freeze(PluginApi);
|
||||
Object.freeze(PluginApi.prototype);
|
||||
|
|
|
@ -99,6 +99,9 @@ export default class extends ContentManager {
|
|||
}
|
||||
|
||||
const plugin = window.require(paths.mainPath)(Plugin, new PluginApi(info), Vendor, deps);
|
||||
if (!(plugin.prototype instanceof Plugin))
|
||||
throw {message: `Plugin ${info.name} did not return a class that extends Plugin.`};
|
||||
|
||||
const instance = new plugin({
|
||||
configs, info, main,
|
||||
paths: {
|
||||
|
|
|
@ -10,10 +10,15 @@
|
|||
|
||||
import { ClientLogger as Logger } from './logger';
|
||||
|
||||
const patchedFunctions = new WeakMap();
|
||||
|
||||
export class PatchedFunction {
|
||||
constructor(object, methodName, replaceOriginal = true) {
|
||||
if (object[methodName].__monkeyPatch)
|
||||
return object[methodName].__monkeyPatch;
|
||||
if (patchedFunctions.has(object[methodName])) {
|
||||
const patchedFunction = patchedFunctions.get(object[methodName]);
|
||||
if (replaceOriginal) patchedFunction.replaceOriginal();
|
||||
return patchedFunction;
|
||||
}
|
||||
|
||||
this.object = object;
|
||||
this.methodName = methodName;
|
||||
|
@ -25,7 +30,9 @@ export class PatchedFunction {
|
|||
this.replace = function(...args) {
|
||||
patchedFunction.call(this, arguments);
|
||||
};
|
||||
this.replace.__monkeyPatch = this;
|
||||
|
||||
patchedFunctions.set(object[methodName], this);
|
||||
patchedFunctions.set(this.replace, this);
|
||||
|
||||
if (replaceOriginal)
|
||||
this.replaceOriginal();
|
||||
|
|
Loading…
Reference in New Issue