Merge pull request #143 from moonlight-mod/nascar
nascar
MODIFIED
packages/browser/src/index.ts
MODIFIED
packages/browser/src/index.ts
@@ -15,6 +15,8 @@ return path.split("/");}window._moonlightBrowserInit = async () => {+ delete window._moonlightBrowserInit;+// Set up a virtual filesystem with IndexedDBawait configure({mounts: {@@ -153,5 +155,5 @@ moonlightNode});// This is set by web-preload for us- await window._moonlightBrowserLoad();+ await window._moonlightWebLoad!();};
MODIFIED
packages/injector/src/index.ts
MODIFIED
packages/injector/src/index.ts
@@ -24,9 +24,13 @@ let isMoonlightDesktop = false;let hasOpenAsar = false;let openAsarConfigPreload: string | undefined;+const scriptUrls = ["web.", "sentry."];+const blockedScripts = new Set<string>();+ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {e.returnValue = oldPreloadPath;});+ipcMain.on(constants.ipcGetAppData, (e) => {e.returnValue = app.getPath("appData");});@@ -73,8 +77,17 @@ blockedUrls = compiled;});function patchCsp(headers: Record<string, string[]>) {- const directives = ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src", "prefetch-src"];- const values = ["*", "blob:", "data:", "'unsafe-inline'", "disclip:"];+ const directives = [+ "script-src",+ "style-src",+ "connect-src",+ "img-src",+ "font-src",+ "media-src",+ "worker-src",+ "prefetch-src"+ ];+ const values = ["*", "blob:", "data:", "'unsafe-inline'", "'unsafe-eval'", "disclip:"];const csp = "content-security-policy";if (headers[csp] == null) return;@@ -110,11 +123,12 @@ }class BrowserWindow extends ElectronBrowserWindow {constructor(opts: BrowserWindowConstructorOptions) {- oldPreloadPath = opts.webPreferences!.preload;-const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;- if (isMainWindow) opts.webPreferences!.preload = require.resolve("./node-preload.js");+ if (isMainWindow) {+ if (!oldPreloadPath) oldPreloadPath = opts.webPreferences!.preload;+ opts.webPreferences!.preload = require.resolve("./node-preload.js");+ }// Event for modifying window optionsmoonlightHost.events.emit("window-options", opts, isMainWindow);@@ -140,9 +154,35 @@ cb({ cancel: false, responseHeaders: details.responseHeaders });}});- // Allow plugins to block some URLs,- // this is needed because multiple webRequest handlers cannot be registered at oncethis.webContents.session.webRequest.onBeforeRequest((details, cb) => {+ /*+ In order to get moonlight loading to be truly async, we prevent Discord+ from loading their scripts immediately. We block the requests, keep note+ of their URLs, and then send them off to node-preload when we get all of+ them. node-preload then loads node side, web side, and then recreates+ the script elements to cause them to re-fetch.++ The browser extension also does this, but in a background script (see+ packages/browser/src/background.js - we should probably get this working+ with esbuild someday).+ */+ if (details.resourceType === "script" && isMainWindow) {+ const hasUrl = scriptUrls.some((url) => details.url.includes(url) && !details.url.includes("?inj"));+ if (hasUrl) blockedScripts.add(details.url);++ if (blockedScripts.size === scriptUrls.length) {+ setTimeout(() => {+ logger.debug("Kicking off node-preload");+ this.webContents.send(constants.ipcNodePreloadKickoff, Array.from(blockedScripts));+ blockedScripts.clear();+ }, 0);+ }++ if (hasUrl) return cb({ cancel: true });+ }++ // Allow plugins to block some URLs,+ // this is needed because multiple webRequest handlers cannot be registered at oncecb({ cancel: blockedUrls.some((u) => u.test(details.url)) });});
MODIFIED
packages/node-preload/src/index.ts
MODIFIED
packages/node-preload/src/index.ts
@@ -13,6 +13,7 @@ import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors";import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";let initialized = false;+let logger: Logger;function setCors() {const data = getDynamicCors();@@ -35,6 +36,8 @@ };let config = await readConfig();initLogger(config);+ logger = new Logger("node-preload");+const extensions = await getExtensions();const processedExtensions = await loadExtensions(extensions);const moonlightDir = await getMoonlightDir();@@ -109,9 +112,12 @@ async function loadPreload() {const webPreloadPath = path.join(__dirname, "web-preload.js");const webPreload = fs.readFileSync(webPreloadPath, "utf8");await webFrame.executeJavaScript(webPreload);++ const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }");+ await func();}-async function init(oldPreloadPath: string) {+async function init() {try {await injectGlobals();await loadPreload();@@ -122,10 +128,49 @@ title: "moonlight node-preload error",message: message});}+}- // Let Discord start even if we fail- if (oldPreloadPath) require(oldPreloadPath);-}+ipcRenderer.on(constants.ipcNodePreloadKickoff, (_, blockedScripts: string[]) => {+ (async () => {+ try {+ await init();+ logger.debug("Blocked scripts:", blockedScripts);++ const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);+ logger.debug("Old preload path:", oldPreloadPath);+ if (oldPreloadPath) require(oldPreloadPath);++ // Do this to get global.DiscordNative assigned+ // @ts-expect-error Lying to discord_desktop_core+ process.emit("loaded");++ function replayScripts() {+ const scripts = [...document.querySelectorAll("script")].filter(+ (script) => script.src && blockedScripts.some((url) => url.includes(script.src))+ );++ blockedScripts.reverse();+ for (const url of blockedScripts) {+ if (url.includes("/sentry.")) continue;-const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);-init(oldPreloadPath);+ const script = scripts.find((script) => url.includes(script.src))!;+ const newScript = document.createElement("script");+ for (const attr of script.attributes) {+ if (attr.name === "src") attr.value += "?inj";+ newScript.setAttribute(attr.name, attr.value);+ }+ script.remove();+ document.documentElement.appendChild(newScript);+ }+ }++ if (document.readyState === "complete") {+ replayScripts();+ } else {+ window.addEventListener("load", replayScripts);+ }+ } catch (e) {+ logger.error("Error restoring original scripts:", e);+ }+ })();+});
MODIFIED
packages/types/src/constants.ts
MODIFIED
packages/types/src/constants.ts
@@ -4,7 +4,9 @@ export const coreExtensionsDir = "core-extensions";export const repoUrlFile = ".moonlight-repo-url";export const installedVersionFile = ".moonlight-installed-version";+export const ipcNodePreloadKickoff = "_moonlight_nodePreloadKickoff";export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";+export const ipcGetAppData = "_moonlight_getAppData";export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";export const ipcMessageBox = "_moonlight_messageBox";
MODIFIED
packages/types/src/index.ts
MODIFIED
packages/types/src/index.ts
@@ -33,6 +33,6 @@ var moonlightNode: MoonlightNode;var moonlightNodeSandboxed: MoonlightNodeSandboxed;var moonlight: MoonlightWeb;- var _moonlightBrowserInit: () => Promise<void>;- var _moonlightBrowserLoad: () => Promise<void>;+ var _moonlightBrowserInit: undefined | (() => Promise<void>);+ var _moonlightWebLoad: undefined | (() => Promise<void>);}
MODIFIED
packages/web-preload/src/index.ts
MODIFIED
packages/web-preload/src/index.ts
@@ -10,6 +10,7 @@ import { createEventEmitter } from "@moonlight-mod/core/util/event";import { EventPayloads, EventType } from "@moonlight-mod/types/core/event";async function load() {+ delete window._moonlightWebLoad;initLogger(moonlightNode.config);const logger = new Logger("web-preload");@@ -49,17 +50,11 @@ } catch (e) {logger.error("Error setting up web-preload", e);}- if (MOONLIGHT_ENV === "web-preload") {- window.addEventListener("DOMContentLoaded", () => {- installStyles();- });+ if (document.readyState === "complete") {+ installStyles();} else {- installStyles();+ window.addEventListener("load", installStyles);}}-if (MOONLIGHT_ENV === "web-preload") {- load();-} else {- window._moonlightBrowserLoad = load;-}+window._moonlightWebLoad = load;