mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
iframe communication working
This commit is contained in:
parent
5ef80495cf
commit
78a1cd3314
@ -48,8 +48,7 @@
|
|||||||
"https://*/*"
|
"https://*/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"contentscript.js",
|
"contentscript.js"
|
||||||
"vendor/ledger/content-script.js"
|
|
||||||
],
|
],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": true
|
"all_frames": true
|
||||||
|
@ -68,8 +68,6 @@ initialize().catch(log.error)
|
|||||||
|
|
||||||
// setup metamask mesh testing container
|
// setup metamask mesh testing container
|
||||||
setupMetamaskMeshMetrics()
|
setupMetamaskMeshMetrics()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object representing a transaction, in whatever state it is in.
|
* An object representing a transaction, in whatever state it is in.
|
||||||
* @typedef TransactionMeta
|
* @typedef TransactionMeta
|
||||||
@ -446,3 +444,4 @@ extension.runtime.onInstalled.addListener(function (details) {
|
|||||||
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
|
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -200,4 +200,4 @@ function redirectToPhishingWarning () {
|
|||||||
console.log('MetaMask - routing to Phishing Warning component')
|
console.log('MetaMask - routing to Phishing Warning component')
|
||||||
const extensionURL = extension.runtime.getURL('phishing.html')
|
const extensionURL = extension.runtime.getURL('phishing.html')
|
||||||
window.location.href = extensionURL
|
window.location.href = extensionURL
|
||||||
}
|
}
|
@ -1,68 +1,182 @@
|
|||||||
const extension = require('extensionizer')
|
const extension = require('extensionizer')
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
|
|
||||||
|
|
||||||
// HD path differs from eth-hd-keyring - MEW, Parity, Geth and Official Ledger clients use same unusual derivation for Ledger
|
// HD path differs from eth-hd-keyring - MEW, Parity, Geth and Official Ledger clients use same unusual derivation for Ledger
|
||||||
const hdPathString = `m/44'/60'/0'`
|
const hdPathString = `m/44'/60'/0'`
|
||||||
const type = 'Ledger Hardware Keyring'
|
const type = 'Ledger Hardware Keyring'
|
||||||
|
const ORIGIN = 'http://localhost:9000'
|
||||||
|
|
||||||
class LedgerKeyring extends EventEmitter {
|
class LedgerKeyring extends EventEmitter {
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
super()
|
super()
|
||||||
this.type = type
|
this.type = type
|
||||||
|
this.page = 0
|
||||||
|
this.perPage = 5
|
||||||
|
this.unlockedAccount = 0
|
||||||
|
this.paths = {}
|
||||||
|
this.iframe = null
|
||||||
|
this.setupIframe()
|
||||||
this.deserialize(opts)
|
this.deserialize(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupIframe(){
|
||||||
|
this.iframe = document.createElement('iframe')
|
||||||
|
this.iframe.src = ORIGIN
|
||||||
|
console.log('Injecting ledger iframe')
|
||||||
|
document.head.appendChild(this.iframe)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Passing messages from iframe to background script
|
||||||
|
*/
|
||||||
|
console.log('[LEDGER]: LEDGER FROM-IFRAME LISTENER READY')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(msg, cb) {
|
||||||
|
console.log('[LEDGER]: SENDING MESSAGE TO IFRAME', msg)
|
||||||
|
this.iframe.contentWindow.postMessage({...msg, target: 'LEDGER-IFRAME'}, '*')
|
||||||
|
window.addEventListener('message', event => {
|
||||||
|
if(event.origin !== ORIGIN) return false
|
||||||
|
if (event.data && event.data.action && event.data.action.search(name) !== -1) {
|
||||||
|
console.log('[LEDGER]: GOT MESAGE FROM IFRAME', event.data)
|
||||||
|
cb(event.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
serialize () {
|
serialize () {
|
||||||
return Promise.resolve({hdPath: this.hdPath, accounts: this.accounts})
|
return Promise.resolve({hdPath: this.hdPath, accounts: this.accounts})
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize (opts = {}) {
|
deserialize (opts = {}) {
|
||||||
this.hdPath = opts.hdPath || hdPathString
|
this.hdPath = opts.hdPath || hdPathString
|
||||||
|
this.unlocked = opts.unlocked || false
|
||||||
this.accounts = opts.accounts || []
|
this.accounts = opts.accounts || []
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
async addAccounts (n = 1) {
|
isUnlocked () {
|
||||||
return new Promise((resolve, reject) => {
|
return this.unlocked
|
||||||
extension.runtime.sendMessage({
|
}
|
||||||
action: 'ledger-add-account',
|
|
||||||
n,
|
|
||||||
})
|
|
||||||
|
|
||||||
extension.runtime.onMessage.addListener(({action, success, payload}) => {
|
setAccountToUnlock (index) {
|
||||||
if (action === 'ledger-sign-transaction') {
|
this.unlockedAccount = parseInt(index, 10)
|
||||||
if (success) {
|
}
|
||||||
resolve(payload)
|
|
||||||
} else {
|
unlock () {
|
||||||
reject(payload)
|
|
||||||
}
|
if (this.isUnlocked()) return Promise.resolve('already unlocked')
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.sendMessage({
|
||||||
|
action: 'ledger-unlock',
|
||||||
|
params: {
|
||||||
|
hdPath: this.hdPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
({action, success, payload}) => {
|
||||||
|
if (success) {
|
||||||
|
resolve(payload)
|
||||||
|
} else {
|
||||||
|
reject(payload)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccounts () {
|
async addAccounts (n = 1) {
|
||||||
return this.accounts.slice()
|
return new Promise((resolve, reject) => {
|
||||||
|
this.unlock()
|
||||||
|
.then(_ => {
|
||||||
|
this.sendMessage({
|
||||||
|
action: 'ledger-add-account',
|
||||||
|
params: {
|
||||||
|
n,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
({action, success, payload}) => {
|
||||||
|
if (success) {
|
||||||
|
resolve(payload)
|
||||||
|
} else {
|
||||||
|
reject(payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getFirstPage () {
|
||||||
|
this.page = 0
|
||||||
|
return this.__getPage(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextPage () {
|
||||||
|
return this.__getPage(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreviousPage () {
|
||||||
|
return this.__getPage(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
__getPage (increment) {
|
||||||
|
|
||||||
|
this.page += increment
|
||||||
|
|
||||||
|
if (this.page <= 0) { this.page = 1 }
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.unlock()
|
||||||
|
.then(_ => {
|
||||||
|
this.sendMessage({
|
||||||
|
action: 'ledger-get-page',
|
||||||
|
params: {
|
||||||
|
page: this.page,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
({action, success, payload}) => {
|
||||||
|
if (success) {
|
||||||
|
resolve(payload)
|
||||||
|
} else {
|
||||||
|
reject(payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccounts () {
|
||||||
|
return Promise.resolve(this.accounts.slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAccount (address) {
|
||||||
|
if (!this.accounts.map(a => a.toLowerCase()).includes(address.toLowerCase())) {
|
||||||
|
throw new Error(`Address ${address} not found in this keyring`)
|
||||||
|
}
|
||||||
|
this.accounts = this.accounts.filter(a => a.toLowerCase() !== address.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
// tx is an instance of the ethereumjs-transaction class.
|
// tx is an instance of the ethereumjs-transaction class.
|
||||||
async signTransaction (address, tx) {
|
async signTransaction (address, tx) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
extension.runtime.sendMessage({
|
this.unlock()
|
||||||
action: 'ledger-sign-transaction',
|
.then(_ => {
|
||||||
address,
|
console.log('[LEDGER]: sending message ', 'ledger-sign-transaction')
|
||||||
tx,
|
this.sendMessage({
|
||||||
})
|
action: 'ledger-sign-transaction',
|
||||||
|
params: {
|
||||||
extension.runtime.onMessage.addListener(({action, success, payload}) => {
|
address,
|
||||||
if (action === 'ledger-sign-transaction') {
|
tx,
|
||||||
if (success) {
|
},
|
||||||
resolve(payload)
|
},
|
||||||
} else {
|
({action, success, payload}) => {
|
||||||
reject(payload)
|
if (success) {
|
||||||
}
|
resolve(payload)
|
||||||
}
|
} else {
|
||||||
|
reject(payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -74,20 +188,23 @@ class LedgerKeyring extends EventEmitter {
|
|||||||
// For personal_sign, we need to prefix the message:
|
// For personal_sign, we need to prefix the message:
|
||||||
async signPersonalMessage (withAccount, message) {
|
async signPersonalMessage (withAccount, message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
extension.runtime.sendMessage({
|
this.unlock()
|
||||||
action: 'ledger-sign-personal-message',
|
.then(_ => {
|
||||||
withAccount,
|
console.log('[LEDGER]: sending message ', 'ledger-sign-personal-message')
|
||||||
message,
|
this.sendMessage({
|
||||||
})
|
action: 'ledger-sign-personal-message',
|
||||||
|
params: {
|
||||||
extension.runtime.onMessage.addListener(({action, success, payload}) => {
|
withAccount,
|
||||||
if (action === 'ledger-sign-personal-message') {
|
message,
|
||||||
if (success) {
|
},
|
||||||
resolve(payload)
|
},
|
||||||
} else {
|
({action, success, payload}) => {
|
||||||
reject(payload)
|
if (success) {
|
||||||
}
|
resolve(payload)
|
||||||
}
|
} else {
|
||||||
|
reject(payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -99,6 +216,14 @@ class LedgerKeyring extends EventEmitter {
|
|||||||
async exportAccount (address) {
|
async exportAccount (address) {
|
||||||
throw new Error('Not supported on this device')
|
throw new Error('Not supported on this device')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forgetDevice () {
|
||||||
|
this.accounts = []
|
||||||
|
this.unlocked = false
|
||||||
|
this.page = 0
|
||||||
|
this.unlockedAccount = 0
|
||||||
|
this.paths = {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LedgerKeyring.type = type
|
LedgerKeyring.type = type
|
||||||
|
40
app/scripts/lib/setupLedgerIframe.js
Normal file
40
app/scripts/lib/setupLedgerIframe.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const extension = require('extensionizer')
|
||||||
|
module.exports = setupLedgerIframe
|
||||||
|
/**
|
||||||
|
* Injects an iframe into the current document to
|
||||||
|
* enable the interaction with ledger devices
|
||||||
|
*/
|
||||||
|
function setupLedgerIframe () {
|
||||||
|
const ORIGIN = 'http://localhost:9000'
|
||||||
|
const ledgerIframe = document.createElement('iframe')
|
||||||
|
ledgerIframe.src = ORIGIN
|
||||||
|
console.log('Injecting ledger iframe')
|
||||||
|
document.head.appendChild(ledgerIframe)
|
||||||
|
|
||||||
|
console.log('[LEDGER]: LEDGER BG LISTENER READY')
|
||||||
|
extension.runtime.onMessage.addListener(({action, params}) => {
|
||||||
|
console.log('[LEDGER]: GOT MSG FROM THE KEYRING', action, params)
|
||||||
|
if (action.search('ledger-') !== -1) {
|
||||||
|
//Forward messages from the keyring to the iframe
|
||||||
|
sendMessage({action, params})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function sendMessage(msg) {
|
||||||
|
ledgerIframe.contentWindow.postMessage({...msg, target: 'LEDGER-IFRAME'}, '*')
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Passing messages from iframe to background script
|
||||||
|
*/
|
||||||
|
console.log('[LEDGER]: LEDGER FROM-IFRAME LISTENER READY')
|
||||||
|
window.addEventListener('message', event => {
|
||||||
|
if(event.origin !== ORIGIN) return false
|
||||||
|
if (event.data && event.data.action && event.data.action.search('ledger-') !== -1) {
|
||||||
|
// Forward messages from the iframe to the keyring
|
||||||
|
console.log('[LEDGER] : forwarding msg', event.data)
|
||||||
|
extension.runtime.sendMessage(event.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
@ -546,12 +546,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
keyringName = TrezorKeyring.type
|
keyringName = TrezorKeyring.type
|
||||||
break
|
break
|
||||||
case 'ledger':
|
case 'ledger':
|
||||||
keyringName = TrezorKeyring.type
|
keyringName = LedgerKeyring.type
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw new Error('MetamaskController:connectHardware - Unknown device')
|
throw new Error('MetamaskController:connectHardware - Unknown device')
|
||||||
}
|
}
|
||||||
|
|
||||||
let keyring = await this.keyringController.getKeyringsByType(keyringName)[0]
|
let keyring = await this.keyringController.getKeyringsByType(keyringName)[0]
|
||||||
if (!keyring) {
|
if (!keyring) {
|
||||||
keyring = await this.keyringController.addNewKeyring(keyringName)
|
keyring = await this.keyringController.addNewKeyring(keyringName)
|
||||||
@ -568,10 +567,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
async connectHardware (deviceName, page) {
|
async connectHardware (deviceName, page) {
|
||||||
|
|
||||||
const oldAccounts = await this.keyringController.getAccounts()
|
|
||||||
const keyring = await this.getKeyringForDevice(deviceName)
|
const keyring = await this.getKeyringForDevice(deviceName)
|
||||||
let accounts = []
|
let accounts = []
|
||||||
|
|
||||||
switch (page) {
|
switch (page) {
|
||||||
case -1:
|
case -1:
|
||||||
accounts = await keyring.getPreviousPage()
|
accounts = await keyring.getPreviousPage()
|
||||||
@ -585,6 +582,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
// Merge with existing accounts
|
// Merge with existing accounts
|
||||||
// and make sure addresses are not repeated
|
// and make sure addresses are not repeated
|
||||||
|
const oldAccounts = await this.keyringController.getAccounts()
|
||||||
const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))]
|
const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))]
|
||||||
this.accountTracker.syncWithAddresses(accountsToTrack)
|
this.accountTracker.syncWithAddresses(accountsToTrack)
|
||||||
return accounts
|
return accounts
|
||||||
|
18
app/vendor/ledger/content-script.js
vendored
18
app/vendor/ledger/content-script.js
vendored
@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
Passing messages from background script to popup
|
|
||||||
*/
|
|
||||||
let port = chrome.runtime.connect({ name: 'ledger' });
|
|
||||||
port.onMessage.addListener(message => {
|
|
||||||
window.postMessage(message, window.location.origin);
|
|
||||||
});
|
|
||||||
port.onDisconnect.addListener(d => {
|
|
||||||
port = null;
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
Passing messages from popup to background script
|
|
||||||
*/
|
|
||||||
window.addEventListener('message', event => {
|
|
||||||
if (port && event.source === window && event.data) {
|
|
||||||
port.postMessage(event.data);
|
|
||||||
}
|
|
||||||
});
|
|
@ -49,7 +49,6 @@ class ConnectHardwareForm extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connectToHardwareWallet = (device) => {
|
connectToHardwareWallet = (device) => {
|
||||||
debugger
|
|
||||||
if (this.state.accounts.length) {
|
if (this.state.accounts.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user