mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Adding flag for MV3 (#14762)
This commit is contained in:
parent
51986a4724
commit
25082ae272
@ -8,7 +8,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="./index-rtl.css" title="rtl" disabled>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app-content"></div>
|
||||
<div id="app-content"><div id="app-loader">Loading...</div></div>
|
||||
<div id="popover-content"></div>
|
||||
<script src="./globalthis.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
81
app/manifest/v3/_base.json
Normal file
81
app/manifest/v3/_base.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"action": {
|
||||
"default_icon": {
|
||||
"16": "images/icon-16.png",
|
||||
"19": "images/icon-19.png",
|
||||
"32": "images/icon-32.png",
|
||||
"38": "images/icon-38.png",
|
||||
"64": "images/icon-64.png",
|
||||
"128": "images/icon-128.png",
|
||||
"512": "images/icon-512.png"
|
||||
},
|
||||
"default_title": "MetaMask",
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"author": "https://metamask.io",
|
||||
"background": {
|
||||
"service_worker": "app-init.js"
|
||||
},
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"windows": "Alt+Shift+M",
|
||||
"mac": "Alt+Shift+M",
|
||||
"chromeos": "Alt+Shift+M",
|
||||
"linux": "Alt+Shift+M"
|
||||
}
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
||||
"js": [
|
||||
"disable-console.js",
|
||||
"globalthis.js",
|
||||
"lockdown-install.js",
|
||||
"lockdown-run.js",
|
||||
"lockdown-more.js",
|
||||
"contentscript.js"
|
||||
],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["*://connect.trezor.io/*/popup.html"],
|
||||
"js": ["vendor/trezor/content-script.js"]
|
||||
}
|
||||
],
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_appDescription__",
|
||||
"icons": {
|
||||
"16": "images/icon-16.png",
|
||||
"19": "images/icon-19.png",
|
||||
"32": "images/icon-32.png",
|
||||
"38": "images/icon-38.png",
|
||||
"48": "images/icon-48.png",
|
||||
"64": "images/icon-64.png",
|
||||
"128": "images/icon-128.png",
|
||||
"512": "images/icon-512.png"
|
||||
},
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_appName__",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"unlimitedStorage",
|
||||
"clipboardWrite",
|
||||
"http://localhost:8545/",
|
||||
"https://*.infura.io/",
|
||||
"https://lattice.gridplus.io/*",
|
||||
"activeTab",
|
||||
"webRequest",
|
||||
"*://*.eth/",
|
||||
"notifications"
|
||||
],
|
||||
"short_name": "__MSG_appName__",
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": ["inpage.js", "phishing.html"],
|
||||
"matches": ["http://*/*", "https://*/*"]
|
||||
}
|
||||
]
|
||||
}
|
1
app/manifest/v3/brave.json
Normal file
1
app/manifest/v3/brave.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
7
app/manifest/v3/chrome.json
Normal file
7
app/manifest/v3/chrome.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"externally_connectable": {
|
||||
"matches": ["https://metamask.io/*"],
|
||||
"ids": ["*"]
|
||||
},
|
||||
"minimum_chrome_version": "66"
|
||||
}
|
26
app/manifest/v3/firefox.json
Normal file
26
app/manifest/v3/firefox.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "webextension@metamask.io",
|
||||
"strict_min_version": "68.0"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"page": "background.html",
|
||||
"persistent": true
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"16": "images/icon-16.png",
|
||||
"19": "images/icon-19.png",
|
||||
"32": "images/icon-32.png",
|
||||
"38": "images/icon-38.png",
|
||||
"64": "images/icon-64.png",
|
||||
"128": "images/icon-128.png",
|
||||
"512": "images/icon-512.png"
|
||||
},
|
||||
"default_title": "MetaMask",
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"manifest_version": 2
|
||||
}
|
9
app/manifest/v3/opera.json
Normal file
9
app/manifest/v3/opera.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": [
|
||||
"storage",
|
||||
"tabs",
|
||||
"clipboardWrite",
|
||||
"clipboardRead",
|
||||
"http://localhost:8545/"
|
||||
]
|
||||
}
|
55
app/scripts/app-init.js
Normal file
55
app/scripts/app-init.js
Normal file
@ -0,0 +1,55 @@
|
||||
// eslint-disable-next-line import/unambiguous
|
||||
function tryImport(...fileNames) {
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
importScripts(...fileNames);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function importAllScripts() {
|
||||
const startImportScriptsTime = Date.now();
|
||||
// applyLavaMoat has been hard coded to "true" as
|
||||
// tryImport('./runtime-cjs.js') is giving issue with XMLHttpRequest object which is not avaialble to service worker.
|
||||
// we need to dynamically inject values of applyLavaMoat once this is fixed.
|
||||
const applyLavaMoat = true;
|
||||
|
||||
tryImport('./globalthis.js');
|
||||
tryImport('./sentry-install.js');
|
||||
|
||||
if (applyLavaMoat) {
|
||||
tryImport('./runtime-lavamoat.js');
|
||||
tryImport('./lockdown-more.js');
|
||||
tryImport('./policy-load.js');
|
||||
} else {
|
||||
tryImport('./lockdown-install.js');
|
||||
tryImport('./lockdown-more.js');
|
||||
tryImport('./lockdown-run.js');
|
||||
tryImport('./runtime-cjs.js');
|
||||
}
|
||||
|
||||
const fileList = [
|
||||
// The list of files is injected at build time by replacing comment below with comma separated strings of file names
|
||||
/** FILE NAMES */
|
||||
];
|
||||
|
||||
fileList.forEach((fileName) => tryImport(fileName));
|
||||
|
||||
// for performance metrics/reference
|
||||
console.log(
|
||||
`SCRIPTS IMPORT COMPLETE in Seconds: ${
|
||||
(Date.now() - startImportScriptsTime) / 1000
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Placing script import call here ensures that scripts are inported each time service worker is activated.
|
||||
importAllScripts();
|
||||
|
||||
/**
|
||||
* An open issue is changes in this file break during hot reloading. Reason is dynamic injection of "FILE NAMES".
|
||||
* Developers need to restart local server if they change this file.
|
||||
*/
|
@ -23,6 +23,7 @@ import {
|
||||
REJECT_NOTFICIATION_CLOSE,
|
||||
REJECT_NOTFICIATION_CLOSE_SIG,
|
||||
} from '../../shared/constants/metametrics';
|
||||
import { isManifestV3 } from '../../shared/modules/mv3.utils';
|
||||
import migrations from './migrations';
|
||||
import Migrator from './lib/migrator';
|
||||
import ExtensionPlatform from './platforms/extension';
|
||||
@ -45,6 +46,14 @@ import { getPlatform } from './lib/util';
|
||||
const { sentry } = global;
|
||||
const firstTimeState = { ...rawFirstTimeState };
|
||||
|
||||
const metamaskInternalProcessHash = {
|
||||
[ENVIRONMENT_TYPE_POPUP]: true,
|
||||
[ENVIRONMENT_TYPE_NOTIFICATION]: true,
|
||||
[ENVIRONMENT_TYPE_FULLSCREEN]: true,
|
||||
};
|
||||
|
||||
const metamaskBlockedPorts = ['trezor-connect'];
|
||||
|
||||
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'info');
|
||||
|
||||
const platform = new ExtensionPlatform();
|
||||
@ -67,8 +76,23 @@ if (inTest || process.env.METAMASK_DEBUG) {
|
||||
global.metamaskGetState = localStore.get.bind(localStore);
|
||||
}
|
||||
|
||||
// initialization flow
|
||||
initialize().catch(log.error);
|
||||
/**
|
||||
* In case of MV3 we attach a "onConnect" event listener as soon as the application is initialised.
|
||||
* Reason is that in case of MV3 a delay in doing this was resulting in missing first connect event after service worker is re-activated.
|
||||
*/
|
||||
|
||||
const initApp = async (remotePort) => {
|
||||
browser.runtime.onConnect.removeListener(initApp);
|
||||
await initialize(remotePort);
|
||||
log.info('MetaMask initialization complete.');
|
||||
};
|
||||
|
||||
if (isManifestV3()) {
|
||||
browser.runtime.onConnect.addListener(initApp);
|
||||
} else {
|
||||
// initialization flow
|
||||
initialize().catch(log.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('../../shared/constants/transaction').TransactionMeta} TransactionMeta
|
||||
@ -128,12 +152,13 @@ initialize().catch(log.error);
|
||||
/**
|
||||
* Initializes the MetaMask controller, and sets up all platform configuration.
|
||||
*
|
||||
* @param {string} remotePort - remote application port connecting to extension.
|
||||
* @returns {Promise} Setup complete.
|
||||
*/
|
||||
async function initialize() {
|
||||
async function initialize(remotePort) {
|
||||
const initState = await loadStateFromPersistence();
|
||||
const initLangCode = await getFirstPreferredLangCode();
|
||||
await setupController(initState, initLangCode);
|
||||
await setupController(initState, initLangCode, remotePort);
|
||||
log.info('MetaMask initialization complete.');
|
||||
}
|
||||
|
||||
@ -205,9 +230,10 @@ async function loadStateFromPersistence() {
|
||||
*
|
||||
* @param {Object} initState - The initial state to start the controller with, matches the state that is emitted from the controller.
|
||||
* @param {string} initLangCode - The region code for the language preferred by the current user.
|
||||
* @param {string} remoteSourcePort - remote application port connecting to extension.
|
||||
* @returns {Promise} After setup is complete.
|
||||
*/
|
||||
function setupController(initState, initLangCode) {
|
||||
function setupController(initState, initLangCode, remoteSourcePort) {
|
||||
//
|
||||
// MetaMask Controller
|
||||
//
|
||||
@ -294,17 +320,13 @@ function setupController(initState, initLangCode) {
|
||||
//
|
||||
// connect to other contexts
|
||||
//
|
||||
if (isManifestV3() && remoteSourcePort) {
|
||||
connectRemote(remoteSourcePort);
|
||||
}
|
||||
|
||||
browser.runtime.onConnect.addListener(connectRemote);
|
||||
browser.runtime.onConnectExternal.addListener(connectExternal);
|
||||
|
||||
const metamaskInternalProcessHash = {
|
||||
[ENVIRONMENT_TYPE_POPUP]: true,
|
||||
[ENVIRONMENT_TYPE_NOTIFICATION]: true,
|
||||
[ENVIRONMENT_TYPE_FULLSCREEN]: true,
|
||||
};
|
||||
|
||||
const metamaskBlockedPorts = ['trezor-connect'];
|
||||
|
||||
const isClientOpenStatus = () => {
|
||||
return (
|
||||
popupIsOpen ||
|
||||
@ -368,6 +390,13 @@ function setupController(initState, initLangCode) {
|
||||
controller.isClientOpen = true;
|
||||
controller.setupTrustedCommunication(portStream, remotePort.sender);
|
||||
|
||||
if (isManifestV3()) {
|
||||
// Message below if captured by UI code in app/scripts/ui.js which will trigger UI initialisation
|
||||
// This ensures that UI is initialised only after background is ready
|
||||
// It fixes the issue of blank screen coming when extension is loaded, the issue is very frequent in MV3
|
||||
remotePort.postMessage({ name: 'CONNECTION_READY' });
|
||||
}
|
||||
|
||||
if (processName === ENVIRONMENT_TYPE_POPUP) {
|
||||
popupIsOpen = true;
|
||||
endOfStream(portStream, () => {
|
||||
@ -480,8 +509,14 @@ function setupController(initState, initLangCode) {
|
||||
if (count) {
|
||||
label = String(count);
|
||||
}
|
||||
browser.browserAction.setBadgeText({ text: label });
|
||||
browser.browserAction.setBadgeBackgroundColor({ color: '#037DD6' });
|
||||
// browserAction has been replaced by action in MV3
|
||||
if (isManifestV3()) {
|
||||
browser.action.setBadgeText({ text: label });
|
||||
browser.action.setBadgeBackgroundColor({ color: '#037DD6' });
|
||||
} else {
|
||||
browser.browserAction.setBadgeText({ text: label });
|
||||
browser.browserAction.setBadgeBackgroundColor({ color: '#037DD6' });
|
||||
}
|
||||
}
|
||||
|
||||
function getUnapprovedTransactionCount() {
|
||||
|
@ -6,6 +6,8 @@ import browser from 'webextension-polyfill';
|
||||
import PortStream from 'extension-port-stream';
|
||||
import { obj as createThoughStream } from 'through2';
|
||||
|
||||
import { isManifestV3 } from '../../shared/modules/mv3.utils';
|
||||
|
||||
// These require calls need to use require to be statically recognized by browserify
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
@ -42,7 +44,12 @@ function injectScript(content) {
|
||||
const container = document.head || document.documentElement;
|
||||
const scriptTag = document.createElement('script');
|
||||
scriptTag.setAttribute('async', 'false');
|
||||
scriptTag.textContent = content;
|
||||
// Inline scripts do not work in MV3 due to more strict security policy
|
||||
if (isManifestV3()) {
|
||||
scriptTag.setAttribute('src', browser.runtime.getURL('inpage.js'));
|
||||
} else {
|
||||
scriptTag.textContent = content;
|
||||
}
|
||||
container.insertBefore(scriptTag, container.children[0]);
|
||||
container.removeChild(scriptTag);
|
||||
} catch (error) {
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
} from '../../shared/constants/app';
|
||||
import { isManifestV3 } from '../../shared/modules/mv3.utils';
|
||||
import ExtensionPlatform from './platforms/extension';
|
||||
import { setupMultiplex } from './lib/stream-utils';
|
||||
import { getEnvironmentType } from './lib/util';
|
||||
@ -35,7 +36,20 @@ async function start() {
|
||||
const connectionStream = new PortStream(extensionPort);
|
||||
|
||||
const activeTab = await queryCurrentActiveTab(windowType);
|
||||
initializeUiWithTab(activeTab);
|
||||
|
||||
/**
|
||||
* In case of MV3 the issue of blank screen was very frequent, it is caused by UI initialising before background is ready to send state.
|
||||
* Code below ensures that UI is rendered only after background is ready.
|
||||
*/
|
||||
if (isManifestV3()) {
|
||||
extensionPort.onMessage.addListener((message) => {
|
||||
if (message?.name === 'CONNECTION_READY') {
|
||||
initializeUiWithTab(activeTab);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
initializeUiWithTab(activeTab);
|
||||
}
|
||||
|
||||
function displayCriticalError(container, err) {
|
||||
container.innerHTML =
|
||||
|
@ -2,7 +2,9 @@ const { promises: fs } = require('fs');
|
||||
const path = require('path');
|
||||
const { mergeWith, cloneDeep } = require('lodash');
|
||||
|
||||
const baseManifest = require('../../app/manifest/_base.json');
|
||||
const baseManifest = process.env.ENABLE_MV3
|
||||
? require('../../app/manifest/v3/_base.json')
|
||||
: require('../../app/manifest/v2/_base.json');
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
|
||||
const { createTask, composeSeries } = require('./task');
|
||||
@ -24,7 +26,7 @@ function createManifestTasks({
|
||||
'..',
|
||||
'..',
|
||||
'app',
|
||||
'manifest',
|
||||
process.env.ENABLE_MV3 ? 'manifest/v3' : 'manifest/v2',
|
||||
`${platform}.json`,
|
||||
),
|
||||
);
|
||||
|
@ -345,6 +345,50 @@ function createScriptTasks({
|
||||
}
|
||||
}
|
||||
|
||||
// Function generates app-init.js for browsers chrome, brave and opera.
|
||||
// It dynamically injects list of files generated in the build.
|
||||
async function bundleMV3AppInitialiser({
|
||||
jsBundles,
|
||||
browserPlatforms,
|
||||
buildType,
|
||||
devMode,
|
||||
ignoredFiles,
|
||||
testing,
|
||||
policyOnly,
|
||||
shouldLintFenceFiles,
|
||||
}) {
|
||||
const label = 'app-init';
|
||||
// TODO: remove this filter for firefox once MV3 is supported in it
|
||||
const mv3BrowserPlatforms = browserPlatforms.filter(
|
||||
(platform) => platform !== 'firefox',
|
||||
);
|
||||
const fileList = jsBundles.reduce(
|
||||
(result, file) => `${result}'${file}',\n `,
|
||||
'',
|
||||
);
|
||||
|
||||
await createNormalBundle({
|
||||
browserPlatforms: mv3BrowserPlatforms,
|
||||
buildType,
|
||||
destFilepath: 'app-init.js',
|
||||
devMode,
|
||||
entryFilepath: './app/scripts/app-init.js',
|
||||
ignoredFiles,
|
||||
label,
|
||||
testing,
|
||||
policyOnly,
|
||||
shouldLintFenceFiles,
|
||||
})();
|
||||
|
||||
mv3BrowserPlatforms.forEach((browser) => {
|
||||
const appInitFile = `./dist/${browser}/app-init.js`;
|
||||
const fileContent = readFileSync('./app/scripts/app-init.js', 'utf8');
|
||||
const fileOutput = fileContent.replace('/** FILE NAMES */', fileList);
|
||||
writeFileSync(appInitFile, fileOutput);
|
||||
});
|
||||
console.log(`Bundle end: service worker app-init.js`);
|
||||
}
|
||||
|
||||
function createFactoredBuild({
|
||||
applyLavaMoat,
|
||||
browserPlatforms,
|
||||
@ -457,7 +501,7 @@ function createFactoredBuild({
|
||||
});
|
||||
|
||||
// wait for bundle completion for postprocessing
|
||||
events.on('bundleDone', () => {
|
||||
events.on('bundleDone', async () => {
|
||||
// Skip HTML generation if nothing is to be written to disk
|
||||
if (policyOnly) {
|
||||
return;
|
||||
@ -503,6 +547,22 @@ function createFactoredBuild({
|
||||
browserPlatforms,
|
||||
applyLavaMoat,
|
||||
});
|
||||
if (process.env.ENABLE_MV3) {
|
||||
const jsBundles = [
|
||||
...commonSet.values(),
|
||||
...groupSet.values(),
|
||||
].map((label) => `./${label}.js`);
|
||||
await bundleMV3AppInitialiser({
|
||||
jsBundles,
|
||||
browserPlatforms,
|
||||
buildType,
|
||||
devMode,
|
||||
ignoredFiles,
|
||||
testing,
|
||||
policyOnly,
|
||||
shouldLintFenceFiles,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'content-script': {
|
||||
|
@ -3511,6 +3511,7 @@
|
||||
"setTimeout": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/snap-controllers>@metamask/controllers": true,
|
||||
"@metamask/controllers": true,
|
||||
"@metamask/post-message-stream": true,
|
||||
"@metamask/providers>@metamask/object-multiplex": true,
|
||||
@ -3533,6 +3534,43 @@
|
||||
"semver": true
|
||||
}
|
||||
},
|
||||
"@metamask/snap-controllers>@metamask/controllers": {
|
||||
"packages": {
|
||||
"@metamask/controllers>isomorphic-fetch": true,
|
||||
"browserify>buffer": true,
|
||||
"ethereumjs-util": true,
|
||||
"ethjs>ethjs-unit": true,
|
||||
"eth-rpc-errors": true,
|
||||
"eth-ens-namehash": true,
|
||||
"eth-sig-util": true,
|
||||
"jsonschema": true,
|
||||
"@metamask/controllers>multiformats": true,
|
||||
"@storybook/api>fast-deep-equal": true,
|
||||
"eth-query": true,
|
||||
"@metamask/controllers>async-mutex": true,
|
||||
"@metamask/snap-controllers>nanoid": true,
|
||||
"immer": true,
|
||||
"web3": true,
|
||||
"single-call-balance-checker-abi": true,
|
||||
"@metamask/metamask-eth-abis": true,
|
||||
"ethereumjs-wallet": true,
|
||||
"eth-keyring-controller": true,
|
||||
"uuid": true,
|
||||
"browserify>events": true,
|
||||
"@metamask/controllers>web3-provider-engine": true,
|
||||
"eth-json-rpc-infura": true,
|
||||
"punycode": true,
|
||||
"@metamask/controllers>eth-phishing-detect": true,
|
||||
"eth-method-registry": true,
|
||||
"@ethereumjs/common": true,
|
||||
"@ethereumjs/tx": true,
|
||||
"@metamask/contract-metadata": true,
|
||||
"@metamask/controllers>abort-controller": true,
|
||||
"ethers": true,
|
||||
"deep-freeze-strict": true,
|
||||
"json-rpc-engine": true
|
||||
}
|
||||
},
|
||||
"@metamask/snap-controllers>@metamask/obs-store": {
|
||||
"packages": {
|
||||
"@metamask/snap-controllers>@metamask/obs-store>through2": true,
|
||||
|
@ -11,6 +11,7 @@
|
||||
"setup:postinstall": "yarn patch-package && yarn allow-scripts",
|
||||
"start": "yarn build:dev dev --apply-lavamoat=false",
|
||||
"start:lavamoat": "yarn build:dev dev --apply-lavamoat=true",
|
||||
"start:mv3": "ENABLE_MV3=true yarn build:dev dev --apply-lavamoat=false",
|
||||
"dist": "yarn build prod",
|
||||
"build": "yarn lavamoat:build",
|
||||
"build:dev": "node development/build/index.js",
|
||||
|
4
shared/modules/mv3.utils.js
Normal file
4
shared/modules/mv3.utils.js
Normal file
@ -0,0 +1,4 @@
|
||||
import browser from 'webextension-polyfill';
|
||||
|
||||
export const isManifestV3 = () =>
|
||||
browser.runtime.getManifest().manifest_version === 3;
|
Loading…
Reference in New Issue
Block a user