Enable loading in context isolation

This commit is contained in:
Zack Rauen 2021-02-28 19:19:25 -05:00
parent f7771f4e4c
commit 3240c6c25b
12 changed files with 122 additions and 97 deletions

View File

@ -10,11 +10,10 @@
}, },
"env": { "env": {
"browser": true, "browser": true,
"node": true, "node": true
"jquery": true
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 2017, "ecmaVersion": 2020,
"sourceType": "module", "sourceType": "module",
"ecmaFeatures": { "ecmaFeatures": {
"jsx": true "jsx": true

View File

@ -6,7 +6,7 @@ const electron = require("electron");
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const BrowserWindow = electron.remote.BrowserWindow; // const BrowserWindow = electron.remote.BrowserWindow;
export default new class ReactDevTools extends Builtin { export default new class ReactDevTools extends Builtin {
get name() {return "ReactDevTools";} get name() {return "ReactDevTools";}
@ -14,41 +14,41 @@ export default new class ReactDevTools extends Builtin {
get id() {return "reactDevTools";} get id() {return "reactDevTools";}
initialize() { initialize() {
super.initialize(); // super.initialize();
this.findExtension(); // this.findExtension();
} }
findExtension() { findExtension() {
let extensionPath = ""; // let extensionPath = "";
if (process.platform === "win32") extensionPath = path.resolve(process.env.LOCALAPPDATA, "Google/Chrome/User Data"); // if (process.platform === "win32") extensionPath = path.resolve(process.env.LOCALAPPDATA, "Google/Chrome/User Data");
else if (process.platform === "linux") extensionPath = path.resolve(process.env.HOME, ".config/google-chrome"); // else if (process.platform === "linux") extensionPath = path.resolve(process.env.HOME, ".config/google-chrome");
else if (process.platform === "darwin") extensionPath = path.resolve(process.env.HOME, "Library/Application Support/Google/Chrome"); // else if (process.platform === "darwin") extensionPath = path.resolve(process.env.HOME, "Library/Application Support/Google/Chrome");
else extensionPath = path.resolve(process.env.HOME, ".config/chromium"); // else extensionPath = path.resolve(process.env.HOME, ".config/chromium");
extensionPath += "/Default/Extensions/fmkadmapgofadopljbjfkapdkoienihi/"; // extensionPath += "/Default/Extensions/fmkadmapgofadopljbjfkapdkoienihi/";
if (fs.existsSync(extensionPath)) { // if (fs.existsSync(extensionPath)) {
const versions = fs.readdirSync(extensionPath); // const versions = fs.readdirSync(extensionPath);
extensionPath = path.resolve(extensionPath, versions[versions.length - 1]); // extensionPath = path.resolve(extensionPath, versions[versions.length - 1]);
} // }
this.extensionPath = extensionPath; // this.extensionPath = extensionPath;
this.isExtensionInstalled = fs.existsSync(extensionPath); // this.isExtensionInstalled = fs.existsSync(extensionPath);
} }
enabled() { enabled() {
if (!this.isExtensionInstalled) this.findExtension(); // if (!this.isExtensionInstalled) this.findExtension();
if (!this.isExtensionInstalled) return Modals.alert(Strings.ReactDevTools.notFound, Strings.ReactDevTools.notFoundDetails); // if (!this.isExtensionInstalled) return Modals.alert(Strings.ReactDevTools.notFound, Strings.ReactDevTools.notFoundDetails);
try { // try {
const didInstall = BrowserWindow.addDevToolsExtension(this.extensionPath); // const didInstall = BrowserWindow.addDevToolsExtension(this.extensionPath);
if (didInstall) this.log("Successfully installed react devtools."); // if (didInstall) this.log("Successfully installed react devtools.");
else this.error("Couldn't find react devtools in chrome extensions!"); // else this.error("Couldn't find react devtools in chrome extensions!");
} // }
catch (err) { // catch (err) {
this.error("Couldn't add the extension!"); // this.error("Couldn't add the extension!");
} // }
} }
disabled() { disabled() {
if (!this.isExtensionInstalled) return; // if (!this.isExtensionInstalled) return;
BrowserWindow.removeDevToolsExtension("React Developer Tools"); // BrowserWindow.removeDevToolsExtension("React Developer Tools");
} }
}; };

View File

@ -25,9 +25,8 @@ export default new class WindowPrefs extends Builtin {
confirmText: Strings.Modals.restartNow, confirmText: Strings.Modals.restartNow,
cancelText: Strings.Modals.restartLater, cancelText: Strings.Modals.restartLater,
onConfirm: () => { onConfirm: () => {
const app = require("electron").remote.app; const ipc = require("electron").ipcRenderer;
app.relaunch(); ipc.send("RELAUNCH");
app.exit();
} }
}); });
} }

View File

@ -2,9 +2,14 @@ export default {
local: false, local: false,
localPath: "", localPath: "",
minified: true, minified: true,
version: "0.6.0",
branch: "stable", branch: "stable",
repo: "rauenzi", repo: "rauenzi",
minSupportedVersion: "0.3.0", minSupportedVersion: "0.3.0",
bdVersion: "1.0.0" bdVersion: "1.0.0",
// Get from main process
version: "0.6.0",
path: "",
appPath: "",
userData: ""
}; };

View File

@ -16,7 +16,7 @@ const React = DiscordModules.React;
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
const Module = require("module").Module; const Module = require("module").Module;
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules")); // Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
const splitRegex = /[^\S\r\n]*?\r?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/; const splitRegex = /[^\S\r\n]*?\r?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/;
const escapedAtRegex = /^\\@/; const escapedAtRegex = /^\\@/;
@ -49,14 +49,14 @@ export default class AddonManager {
this.windows = new Set(); this.windows = new Set();
} }
initialize() { async initialize() {
this.originalRequire = Module._extensions[this.moduleExtension]; this.originalRequire = Module._extensions[this.moduleExtension];
Module._extensions[this.moduleExtension] = this.getAddonRequire(); Module._extensions[this.moduleExtension] = this.getAddonRequire();
Settings.on(this.collection, this.category, this.id, (enabled) => { Settings.on(this.collection, this.category, this.id, (enabled) => {
if (enabled) this.watchAddons(); if (enabled) this.watchAddons();
else this.unwatchAddons(); else this.unwatchAddons();
}); });
return this.loadAllAddons(); return await this.loadAllAddons();
} }
// Subclasses should overload this and modify the addon object as needed to fully load it // Subclasses should overload this and modify the addon object as needed to fully load it
@ -196,12 +196,21 @@ export default class AddonManager {
} }
// Subclasses should use the return (if not AddonError) and push to this.addonList // Subclasses should use the return (if not AddonError) and push to this.addonList
loadAddon(filename, shouldToast = false) { async loadAddon(filename, shouldToast = false) {
if (typeof(filename) === "undefined") return; if (typeof(filename) === "undefined") return;
try {__non_webpack_require__(path.resolve(this.addonFolder, filename));} try {
catch (error) {return new AddonError(filename, filename, Strings.Addons.compileError, {message: error.message, stack: error.stack});} const addon = __non_webpack_require__(path.resolve(this.addonFolder, filename));
await Promise.resolve(addon);
}
catch (error) {
return new AddonError(filename, filename, Strings.Addons.compileError, {message: error.message, stack: error.stack});
}
const addon = __non_webpack_require__(path.resolve(this.addonFolder, filename)); const addon = __non_webpack_require__(path.resolve(this.addonFolder, filename));
// console.log(addon);
// await Promise.resolve(addon);
// addon = __non_webpack_require__(path.resolve(this.addonFolder, filename));
// console.log(addon);
if (this.addonList.find(c => c.id == addon.id)) return new AddonError(addon.name, filename, Strings.Addons.alreadyExists.format({type: this.prefix, name: addon.name})); if (this.addonList.find(c => c.id == addon.id)) return new AddonError(addon.name, filename, Strings.Addons.alreadyExists.format({type: this.prefix, name: addon.name}));
const error = this.initializeAddon(addon); const error = this.initializeAddon(addon);
@ -226,11 +235,11 @@ export default class AddonManager {
return true; return true;
} }
reloadAddon(idOrFileOrAddon, shouldToast = true) { async reloadAddon(idOrFileOrAddon, shouldToast = true) {
const addon = typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon; const addon = typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
const didUnload = this.unloadAddon(addon, shouldToast, true); const didUnload = this.unloadAddon(addon, shouldToast, true);
if (addon && !didUnload) return didUnload; if (addon && !didUnload) return didUnload;
return this.loadAddon(addon ? addon.filename : idOrFileOrAddon, shouldToast); return await this.loadAddon(addon ? addon.filename : idOrFileOrAddon, shouldToast);
} }
isLoaded(idOrFile) { isLoaded(idOrFile) {
@ -285,7 +294,7 @@ export default class AddonManager {
for (const name of results.removed) this.unloadAddon(name); for (const name of results.removed) this.unloadAddon(name);
} }
loadAllAddons() { async loadAllAddons() {
this.loadState(); this.loadState();
const errors = []; const errors = [];
const files = fs.readdirSync(this.addonFolder); const files = fs.readdirSync(this.addonFolder);
@ -313,7 +322,7 @@ export default class AddonManager {
// Rename the file and let it go on // Rename the file and let it go on
fs.renameSync(absolutePath, path.resolve(this.addonFolder, newFilename)); fs.renameSync(absolutePath, path.resolve(this.addonFolder, newFilename));
} }
const addon = this.loadAddon(filename, false); const addon = await this.loadAddon(filename, false);
if (addon instanceof AddonError) errors.push(addon); if (addon instanceof AddonError) errors.push(addon);
} }

View File

@ -31,18 +31,15 @@ export default new class Core {
}); });
ipcRenderer.invoke("bd-injector-info").then(injectorInfo => { ipcRenderer.invoke("bd-injector-info").then(injectorInfo => {
Config.version = injectorInfo.version; Config.version = injectorInfo.version;
Config.path = injectorInfo.path;
Config.appPath = injectorInfo.appPath;
Config.userData = injectorInfo.userData;
this.checkInjectorUpdate(); this.checkInjectorUpdate();
}); });
} }
get dependencies() { get dependencies() {
return [ return [
{
name: "jquery",
type: "script",
url: "https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js",
backup: "https://cdn.jsdelivr.net/gh/jquery/jquery@2.0.0/jquery.min.js"
},
{ {
name: "bd-stylesheet", name: "bd-stylesheet",
type: "style", type: "style",
@ -85,6 +82,7 @@ export default new class Core {
await dependencyPromise; await dependencyPromise;
Logger.log("Startup", "Loading Plugins"); Logger.log("Startup", "Loading Plugins");
// const pluginErrors = [];
const pluginErrors = PluginManager.initialize(); const pluginErrors = PluginManager.initialize();
Logger.log("Startup", "Loading Themes"); Logger.log("Startup", "Loading Themes");
@ -175,9 +173,8 @@ export default new class Core {
try { try {
const didUpdate = await this.updateInjector(); const didUpdate = await this.updateInjector();
if (!didUpdate) return onUpdateFailed(); if (!didUpdate) return onUpdateFailed();
const app = require("electron").remote.app; const ipc = require("electron").ipcRenderer;
app.relaunch(); ipc.send("RELAUNCH");
app.exit();
} }
catch (err) { catch (err) {
onUpdateFailed(); onUpdateFailed();

View File

@ -3,7 +3,8 @@ import Utilities from "./utilities";
import Logger from "./logger"; import Logger from "./logger";
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable"; const releaseChannel = window?.DiscordNative?.app?.getReleaseChannel?.() ?? "stable";
const discordVersion = window?.DiscordNative?.remoteApp?.getVersion?.() ?? "0.0.309";
// Schema // Schema
// ======================= // =======================
@ -82,10 +83,9 @@ export default new class DataStore {
get injectionPath() { get injectionPath() {
if (this._injectionPath) return this._injectionPath; if (this._injectionPath) return this._injectionPath;
const electron = require("electron").remote.app; const base = Config.appPath;
const base = electron.getAppPath(); const roamingBase = Config.userData;
const roamingBase = electron.getPath("userData"); const roamingLocation = path.resolve(roamingBase, discordVersion, "modules", "discord_desktop_core", "injector");
const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector");
const location = path.resolve(base, "..", "app"); const location = path.resolve(base, "..", "app");
const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null; const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null;
if (!realLocation) return this._injectionPath = null; if (!realLocation) return this._injectionPath = null;

View File

@ -744,13 +744,4 @@ export default class DOMTools {
for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]); for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]);
return domWrapper; return domWrapper;
} }
/**
* Resolves the node to an HTMLElement. This is mainly used by library modules.
* @param {(jQuery|Element)} node - node to resolve
*/
static resolveElement(node) {
if (!(node instanceof jQuery) && !(node instanceof Element)) return undefined;
return node instanceof jQuery ? node[0] : node;
}
} }

View File

@ -137,8 +137,8 @@ BdApi.findModuleByDisplayName = function(name) {
// Gets react instance // Gets react instance
BdApi.getInternalInstance = function(node) { BdApi.getInternalInstance = function(node) {
if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined; // if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
if (node instanceof jQuery) node = node[0]; // if (node instanceof jQuery) node = node[0];
return Utilities.getReactInstance(node); return Utilities.getReactInstance(node);
}; };

View File

@ -10,7 +10,12 @@ import Modals from "../ui/modals";
import SettingsRenderer from "../ui/settings"; import SettingsRenderer from "../ui/settings";
const path = require("path"); const path = require("path");
const electronRemote = require("electron").remote; const electron = require("electron");
const ipc = electron.ipcRenderer;
const vm = require("vm");
// const electronRemote = require("electron").remote;
// window.$ = window.jQuery = function() {}
export default new class PluginManager extends AddonManager { export default new class PluginManager extends AddonManager {
get name() {return "PluginManager";} get name() {return "PluginManager";}
@ -23,6 +28,7 @@ export default new class PluginManager extends AddonManager {
constructor() { constructor() {
super(); super();
this.promises = {};
this.onSwitch = this.onSwitch.bind(this); this.onSwitch = this.onSwitch.bind(this);
this.observer = new MutationObserver((mutations) => { this.observer = new MutationObserver((mutations) => {
for (let i = 0, mlen = mutations.length; i < mlen; i++) { for (let i = 0, mlen = mutations.length; i < mlen; i++) {
@ -31,8 +37,8 @@ export default new class PluginManager extends AddonManager {
}); });
} }
initialize() { async initialize() {
const errors = super.initialize(); const errors = await super.initialize();
this.setupFunctions(); this.setupFunctions();
Settings.registerPanel("plugins", Strings.Panels.plugins, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.plugins, this.addonList, this.state, { Settings.registerPanel("plugins", Strings.Panels.plugins, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.plugins, this.addonList, this.state, {
type: this.prefix, type: this.prefix,
@ -59,13 +65,13 @@ export default new class PluginManager extends AddonManager {
unloadPlugin(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);} unloadPlugin(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);}
loadPlugin(filename) {return this.loadAddon(filename);} loadPlugin(filename) {return this.loadAddon(filename);}
loadAddon(filename) { async loadAddon(filename) {
const error = super.loadAddon(filename); const error = await super.loadAddon(filename);
if (error) Modals.showAddonErrors({plugins: [error]}); if (error) Modals.showAddonErrors({plugins: [error]});
} }
reloadPlugin(idOrFileOrAddon) { async reloadPlugin(idOrFileOrAddon) {
const error = this.reloadAddon(idOrFileOrAddon); const error = await this.reloadAddon(idOrFileOrAddon);
if (error) Modals.showAddonErrors({plugins: [error]}); if (error) Modals.showAddonErrors({plugins: [error]});
return typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon; return typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
} }
@ -94,9 +100,27 @@ export default new class PluginManager extends AddonManager {
getFileModification(module, fileContent, meta) { 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};}`; fileContent += `\nif (module.exports.default) {module.exports = module.exports.default;}\nif (!module.exports.prototype || !module.exports.prototype.start) {module.exports = ${meta.exports || meta.name};}`;
module._compile(fileContent, module.filename);
meta.exports = module.exports; window.global = window;
module.exports = meta; window.module = module;
window.__filename = path.basename(module.filename);
window.__dirname = this.addonFolder;
const wrapped = `(${vm.compileFunction(fileContent, ["exports", "require", "module", "__filename", "__dirname"])})`;
// console.log(module);
module.exports = new Promise(resolve => {
ipc.invoke("EXEC_JS", `${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 ""; return "";
} }
@ -145,7 +169,8 @@ export default new class PluginManager extends AddonManager {
} }
setupFunctions() { setupFunctions() {
electronRemote.getCurrentWebContents().on("did-navigate-in-page", this.onSwitch.bind(this)); // electronRemote.getCurrentWebContents().on("did-navigate-in-page", this.onSwitch.bind(this));
ipc.on("DID_NAVIGATE_IN_PAGE", this.onSwitch);
this.observer.observe(document, { this.observer.observe(document, {
childList: true, childList: true,
subtree: true subtree: true

View File

@ -19,8 +19,8 @@ export default new class ThemeManager extends AddonManager {
get prefix() {return "theme";} get prefix() {return "theme";}
get language() {return "css";} get language() {return "css";}
initialize() { async initialize() {
const errors = super.initialize(); const errors = await super.initialize();
Settings.registerPanel("themes", Strings.Panels.themes, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.themes, this.addonList, this.state, { Settings.registerPanel("themes", Strings.Panels.themes, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.themes, this.addonList, this.state, {
type: this.prefix, type: this.prefix,
folder: this.addonFolder, folder: this.addonFolder,
@ -45,10 +45,10 @@ export default new class ThemeManager extends AddonManager {
unloadTheme(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);} unloadTheme(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);}
loadTheme(filename) {return this.loadAddon(filename);} loadTheme(filename) {return this.loadAddon(filename);}
reloadTheme(idOrFileOrAddon) {return this.reloadAddon(idOrFileOrAddon);} async reloadTheme(idOrFileOrAddon) {return await this.reloadAddon(idOrFileOrAddon);}
loadAddon(filename) { async loadAddon(filename) {
const error = super.loadAddon(filename); const error = await super.loadAddon(filename);
if (error) Modals.showAddonErrors({themes: [error]}); if (error) Modals.showAddonErrors({themes: [error]});
} }

View File

@ -4,7 +4,7 @@ const SortedGuildStore = WebpackModules.getByProps("getSortedGuilds");
const AvatarDefaults = WebpackModules.getByProps("getUserAvatarURL", "DEFAULT_AVATARS"); const AvatarDefaults = WebpackModules.getByProps("getUserAvatarURL", "DEFAULT_AVATARS");
const InviteActions = WebpackModules.getByProps("acceptInvite"); const InviteActions = WebpackModules.getByProps("acceptInvite");
const BrowserWindow = require("electron").remote.BrowserWindow; // const BrowserWindow = require("electron").remote.BrowserWindow;
const betterDiscordServer = { const betterDiscordServer = {
name: "BetterDiscord", name: "BetterDiscord",
@ -140,16 +140,16 @@ export default new class PublicServersConnection {
} }
connect() { connect() {
return new Promise(resolve => { // return new Promise(resolve => {
const joinWindow = new BrowserWindow(this.windowOptions); // const joinWindow = new BrowserWindow(this.windowOptions);
const url = `https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=${this.connectEndPoint}`; // const url = `https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=${this.connectEndPoint}`;
joinWindow.webContents.on("did-navigate", (event, navUrl) => { // joinWindow.webContents.on("did-navigate", (event, navUrl) => {
if (navUrl != this.connectEndPoint) return; // if (navUrl != this.connectEndPoint) return;
joinWindow.close(); // joinWindow.close();
resolve(); // resolve();
}); // });
joinWindow.loadURL(url); // joinWindow.loadURL(url);
}); // });
} }
get windowOptions() { get windowOptions() {