1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 19:26:13 +02:00
metamask-extension/app/scripts/platforms/extension.js
Mark Stacey 864800b035
Normalize extension verison to SemVer (#12254)
The extension version used throughout the wallet is now normalized to a
SemVer-compliant version that matches the version used in
`package.json`. We use this version for display on the "About" page,
and we attach it to all error reports and metric events, so it's
important that we format it consistently so that we can correlate
events on the same version across different browsers.

This normalization step is necessary because Firefox and Chrome both
have different requirements for the extension version, and neither is
SemVer-compliant.
2021-10-14 18:50:14 -02:30

264 lines
7.0 KiB
JavaScript

import extension from 'extensionizer';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import { getEnvironmentType, checkForError } from '../lib/util';
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction';
export default class ExtensionPlatform {
//
// Public
//
reload() {
extension.runtime.reload();
}
openTab(options) {
return new Promise((resolve, reject) => {
extension.tabs.create(options, (newTab) => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve(newTab);
});
});
}
openWindow(options) {
return new Promise((resolve, reject) => {
extension.windows.create(options, (newWindow) => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve(newWindow);
});
});
}
focusWindow(windowId) {
return new Promise((resolve, reject) => {
extension.windows.update(windowId, { focused: true }, () => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve();
});
});
}
updateWindowPosition(windowId, left, top) {
return new Promise((resolve, reject) => {
extension.windows.update(windowId, { left, top }, () => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve();
});
});
}
getLastFocusedWindow() {
return new Promise((resolve, reject) => {
extension.windows.getLastFocused((windowObject) => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve(windowObject);
});
});
}
closeCurrentWindow() {
return extension.windows.getCurrent((windowDetails) => {
return extension.windows.remove(windowDetails.id);
});
}
getVersion() {
const {
version,
version_name: versionName,
} = extension.runtime.getManifest();
const versionParts = version.split('.');
if (versionName) {
// On Chrome, the build type is stored as `version_name` in the manifest, and the fourth part
// of the version is the build version.
const buildType = versionName;
if (versionParts.length < 4) {
throw new Error(`Version missing build number: '${version}'`);
}
const [major, minor, patch, buildVersion] = versionParts;
return `${major}.${minor}.${patch}-${buildType}.${buildVersion}`;
} else if (versionParts.length === 4) {
// On Firefox, the build type and build version are in the fourth part of the version.
const [major, minor, patch, prerelease] = versionParts;
const matches = prerelease.match(/^(\w+)(\d)+$/u);
if (matches === null) {
throw new Error(`Version contains invalid prerelease: ${version}`);
}
const [, buildType, buildVersion] = matches;
return `${major}.${minor}.${patch}-${buildType}.${buildVersion}`;
}
// If there is no `version_name` and there are only 3 version parts, then this is not a
// prerelease and the version requires no modification.
return version;
}
openExtensionInBrowser(route = null, queryString = null) {
let extensionURL = extension.runtime.getURL('home.html');
if (queryString) {
extensionURL += `?${queryString}`;
}
if (route) {
extensionURL += `#${route}`;
}
this.openTab({ url: extensionURL });
if (getEnvironmentType() !== ENVIRONMENT_TYPE_BACKGROUND) {
window.close();
}
}
getPlatformInfo(cb) {
try {
extension.runtime.getPlatformInfo((platform) => {
cb(null, platform);
});
} catch (e) {
cb(e);
// eslint-disable-next-line no-useless-return
return;
}
}
showTransactionNotification(txMeta, rpcPrefs) {
const { status, txReceipt: { status: receiptStatus } = {} } = txMeta;
if (status === TRANSACTION_STATUSES.CONFIRMED) {
// There was an on-chain failure
receiptStatus === '0x0'
? this._showFailedTransaction(
txMeta,
'Transaction encountered an error.',
)
: this._showConfirmedTransaction(txMeta, rpcPrefs);
} else if (status === TRANSACTION_STATUSES.FAILED) {
this._showFailedTransaction(txMeta);
}
}
getAllWindows() {
return new Promise((resolve, reject) => {
extension.windows.getAll((windows) => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve(windows);
});
});
}
getActiveTabs() {
return new Promise((resolve, reject) => {
extension.tabs.query({ active: true }, (tabs) => {
const error = checkForError();
if (error) {
return reject(error);
}
return resolve(tabs);
});
});
}
currentTab() {
return new Promise((resolve, reject) => {
extension.tabs.getCurrent((tab) => {
const err = checkForError();
if (err) {
reject(err);
} else {
resolve(tab);
}
});
});
}
switchToTab(tabId) {
return new Promise((resolve, reject) => {
extension.tabs.update(tabId, { highlighted: true }, (tab) => {
const err = checkForError();
if (err) {
reject(err);
} else {
resolve(tab);
}
});
});
}
closeTab(tabId) {
return new Promise((resolve, reject) => {
extension.tabs.remove(tabId, () => {
const err = checkForError();
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
_showConfirmedTransaction(txMeta, rpcPrefs) {
this._subscribeToNotificationClicked();
const url = getBlockExplorerLink(txMeta, rpcPrefs);
const nonce = parseInt(txMeta.txParams.nonce, 16);
const title = 'Confirmed transaction';
const message = `Transaction ${nonce} confirmed! ${
url.length ? 'View on Etherscan' : ''
}`;
this._showNotification(title, message, url);
}
_showFailedTransaction(txMeta, errorMessage) {
const nonce = parseInt(txMeta.txParams.nonce, 16);
const title = 'Failed transaction';
const message = `Transaction ${nonce} failed! ${
errorMessage || txMeta.err.message
}`;
this._showNotification(title, message);
}
_showNotification(title, message, url) {
extension.notifications.create(url, {
type: 'basic',
title,
iconUrl: extension.extension.getURL('../../images/icon-64.png'),
message,
});
}
_subscribeToNotificationClicked() {
if (!extension.notifications.onClicked.hasListener(this._viewOnEtherscan)) {
extension.notifications.onClicked.addListener(this._viewOnEtherscan);
}
}
_viewOnEtherscan(txId) {
if (txId.startsWith('https://')) {
extension.tabs.create({ url: txId });
}
}
}