diff --git a/app/manifest/v2/_base.json b/app/manifest/v2/_base.json index bc8cfc6e3533..c0b25efeeb94 100644 --- a/app/manifest/v2/_base.json +++ b/app/manifest/v2/_base.json @@ -35,7 +35,8 @@ "lockdown-install.js", "lockdown-run.js", "lockdown-more.js", - "contentscript.js" + "contentscript.js", + "inpage.js" ], "run_at": "document_start", "all_frames": true diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index dda5e7a74efe..8d63ec70ac5e 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -9,16 +9,6 @@ import { checkForLastError } from '../../shared/modules/browser-runtime.utils'; import { isManifestV3 } from '../../shared/modules/mv3.utils'; import shouldInjectProvider from '../../shared/modules/provider-injection'; -// These require calls need to use require to be statically recognized by browserify -const fs = require('fs'); -const path = require('path'); - -const inpageContent = fs.readFileSync( - path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js'), - 'utf8', -); -const inpageBundle = inpageContent; - // contexts const CONTENT_SCRIPT = 'metamask-contentscript'; const INPAGE = 'metamask-inpage'; @@ -61,24 +51,6 @@ let extensionMux, pageMux, pageChannel; -/** - * Injects a script tag into the current document - * - * @param {string} content - Code to be executed in the current document - */ -function injectScript(content) { - try { - const container = document.head || document.documentElement; - const scriptTag = document.createElement('script'); - scriptTag.setAttribute('async', 'false'); - scriptTag.textContent = content; - container.insertBefore(scriptTag, container.children[0]); - container.removeChild(scriptTag); - } catch (error) { - console.error('MetaMask: Provider injection failed.', error); - } -} - /** * PHISHING STREAM LOGIC */ @@ -545,9 +517,6 @@ const start = () => { } if (shouldInjectProvider()) { - if (!isManifestV3) { - injectScript(inpageBundle); - } initStreams(); // https://bugs.chromium.org/p/chromium/issues/detail?id=1457040 diff --git a/development/build/scripts.js b/development/build/scripts.js index eeb61bdb8f87..cd641658cc88 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -2,7 +2,7 @@ const { callbackify } = require('util'); const path = require('path'); -const { writeFileSync, readFileSync } = require('fs'); +const { writeFileSync, readFileSync, unlinkSync } = require('fs'); const EventEmitter = require('events'); const assert = require('assert'); const gulp = require('gulp'); @@ -43,6 +43,7 @@ const { getBuildName, getBuildAppId, getBuildIcon, + makeSelfInjecting, } = require('./utils'); const { @@ -471,6 +472,26 @@ function createScriptTasks({ version, applyLavaMoat, }), + () => { + // MV3 injects inpage into the tab's main world, but in MV2 we need + // to do it manually: + if (process.env.ENABLE_MV3) { + return; + } + // stringify inpage.js into itself, and then make it inject itself into the page + browserPlatforms.forEach((browser) => { + makeSelfInjecting( + path.join(__dirname, `../../dist/${browser}/${inpage}.js`), + ); + }); + // delete the inpage.js source map, as it no longer represents inpage.js + // and so `yarn source-map-explorer` can't handle it. It's also not + // useful anyway, as inpage.js is injected as a `script.textContent`, + // and not tracked in Sentry or browsers devtools anyway. + unlinkSync( + path.join(__dirname, `../../dist/sourcemaps/${inpage}.js.map`), + ); + }, createNormalBundle({ buildTarget, buildType, diff --git a/development/build/utils.js b/development/build/utils.js index eeba5ae67cc9..07349c88db28 100644 --- a/development/build/utils.js +++ b/development/build/utils.js @@ -1,5 +1,5 @@ const path = require('path'); -const { readFileSync } = require('fs'); +const { readFileSync, writeFileSync } = require('fs'); const semver = require('semver'); const { capitalize } = require('lodash'); const { loadBuildTypesConfig } = require('../lib/build-type'); @@ -287,6 +287,21 @@ function getBuildIcon({ buildType }) { const svg = readFileSync(svgLogoPath, 'utf8'); return `data:image/svg+xml,${encodeURIComponent(svg)}`; } +/** + * Takes the given JavaScript file at `filePath` and replaces its contents with + * a script that injects the original file contents into the document in which + * the file is loaded. Useful for MV2 extensions to run scripts synchronously in the + * "MAIN" world. + * + * @param {string} filePath - The path to the file to convert to a self-injecting + * script. + */ +function makeSelfInjecting(filePath) { + const fileContents = readFileSync(filePath, 'utf8'); + const textContent = JSON.stringify(fileContents); + const js = `{let d=document,s=d.createElement('script');s.textContent=${textContent};d.documentElement.appendChild(s).remove();}`; + writeFileSync(filePath, js, 'utf8'); +} module.exports = { getBrowserVersionMap, @@ -299,4 +314,5 @@ module.exports = { logError, getPathInsideNodeModules, wrapAgainstScuttling, + makeSelfInjecting, }; diff --git a/development/sourcemap-validator.js b/development/sourcemap-validator.js index 87c74a1236f8..585144100942 100755 --- a/development/sourcemap-validator.js +++ b/development/sourcemap-validator.js @@ -25,8 +25,8 @@ async function start() { `common-0.js`, `background-0.js`, `ui-0.js`, - // `contentscript.js`, skipped because the validator is erroneously sampling the inlined `inpage.js` script - `inpage.js`, + `contentscript.js`, + // `inpage.js`, skipped because the validator can't sample the inlined `inpage.js` script ]; let valid = true;