diff options
Diffstat (limited to 'app/scripts/background.js')
-rw-r--r-- | app/scripts/background.js | 253 |
1 files changed, 119 insertions, 134 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js index f3837a028..2e5a992b9 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,162 +1,147 @@ const urlUtil = require('url') -const extend = require('xtend') -const Dnode = require('dnode') -const eos = require('end-of-stream') +const endOfStream = require('end-of-stream') +const asyncQ = require('async-q') +const pipe = require('pump') +const LocalStorageStore = require('obs-store/lib/localStorage') +const storeTransform = require('obs-store/lib/transform') +const Migrator = require('./lib/migrator/') +const migrations = require('./migrations/') const PortStream = require('./lib/port-stream.js') const notification = require('./lib/notifications.js') -const messageManager = require('./lib/message-manager') -const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex const MetamaskController = require('./metamask-controller') const extension = require('./lib/extension') +const firstTimeState = require('./first-time-state') const STORAGE_KEY = 'metamask-config' const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' -var popupIsOpen = false - -const controller = new MetamaskController({ - // User confirmation callbacks: - showUnconfirmedMessage: triggerUi, - unlockAccountMessage: triggerUi, - showUnapprovedTx: triggerUi, - // Persistence Methods: - setData, - loadData, -}) -function triggerUi () { - if (!popupIsOpen) notification.show() -} -// On first install, open a window to MetaMask website to how-it-works. -extension.runtime.onInstalled.addListener(function (details) { - if ((details.reason === 'install') && (!METAMASK_DEBUG)) { - extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) - } -}) +let popupIsOpen = false + +// state persistence +const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) + +// initialization flow +asyncQ.waterfall([ + () => loadStateFromPersistence(), + (initState) => setupController(initState), +]) +.then(() => console.log('MetaMask initialization complete.')) +.catch((err) => { console.error(err) }) // -// connect to other contexts +// State and Persistence // -extension.runtime.onConnect.addListener(connectRemote) -function connectRemote (remotePort) { - var isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification' - var portStream = new PortStream(remotePort) - if (isMetaMaskInternalProcess) { - // communication with popup - popupIsOpen = remotePort.name === 'popup' - setupTrustedCommunication(portStream, 'MetaMask', remotePort.name) - } else { - // communication with page - var originDomain = urlUtil.parse(remotePort.sender.url).hostname - setupUntrustedCommunication(portStream, originDomain) - } -} - -function setupUntrustedCommunication (connectionStream, originDomain) { - // setup multiplexing - var mx = setupMultiplex(connectionStream) - // connect features - controller.setupProviderConnection(mx.createStream('provider'), originDomain) - controller.setupPublicConfig(mx.createStream('publicConfig')) +function loadStateFromPersistence() { + // migrations + let migrator = new Migrator({ migrations }) + let initialState = migrator.generateInitialState(firstTimeState) + return asyncQ.waterfall([ + // read from disk + () => Promise.resolve(diskStore.getState() || initialState), + // migrate data + (versionedData) => migrator.migrateData(versionedData), + // write to disk + (versionedData) => { + diskStore.putState(versionedData) + return Promise.resolve(versionedData) + }, + // resolve to just data + (versionedData) => Promise.resolve(versionedData.data), + ]) } -function setupTrustedCommunication (connectionStream, originDomain) { - // setup multiplexing - var mx = setupMultiplex(connectionStream) - // connect features - setupControllerConnection(mx.createStream('controller')) - controller.setupProviderConnection(mx.createStream('provider'), originDomain) -} +function setupController (initState) { -// -// remote features -// + // + // MetaMask Controller + // -function setupControllerConnection (stream) { - controller.stream = stream - var api = controller.getApi() - var dnode = Dnode(api) - stream.pipe(dnode).pipe(stream) - dnode.on('remote', (remote) => { - // push updates to popup - var sendUpdate = remote.sendUpdate.bind(remote) - controller.on('update', sendUpdate) - // teardown on disconnect - eos(stream, () => { - controller.removeListener('update', sendUpdate) - popupIsOpen = false - }) + const controller = new MetamaskController({ + // User confirmation callbacks: + showUnconfirmedMessage: triggerUi, + unlockAccountMessage: triggerUi, + showUnapprovedTx: triggerUi, + // initial state + initState, }) -} - -// -// plugin badge text -// + global.metamaskController = controller + + // setup state persistence + pipe( + controller.store, + storeTransform(versionifyData), + diskStore + ) + + function versionifyData(state) { + let versionedData = diskStore.getState() + versionedData.data = state + return versionedData + } -controller.txManager.on('updateBadge', updateBadge) -updateBadge() - -function updateBadge () { - var label = '' - var unapprovedTxCount = controller.txManager.unapprovedTxCount - var unconfMsgs = messageManager.unconfirmedMsgs() - var unconfMsgLen = Object.keys(unconfMsgs).length - var count = unapprovedTxCount + unconfMsgLen - if (count) { - label = String(count) + // + // connect to other contexts + // + + extension.runtime.onConnect.addListener(connectRemote) + function connectRemote (remotePort) { + var isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification' + var portStream = new PortStream(remotePort) + if (isMetaMaskInternalProcess) { + // communication with popup + popupIsOpen = popupIsOpen || (remotePort.name === 'popup') + controller.setupTrustedCommunication(portStream, 'MetaMask', remotePort.name) + // record popup as closed + if (remotePort.name === 'popup') { + endOfStream(portStream, () => { + popupIsOpen = false + }) + } + } else { + // communication with page + var originDomain = urlUtil.parse(remotePort.sender.url).hostname + controller.setupUntrustedCommunication(portStream, originDomain) + } } - extension.browserAction.setBadgeText({ text: label }) - extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' }) -} -// data :: setters/getters + // + // User Interface setup + // + + updateBadge() + controller.txManager.on('updateBadge', updateBadge) + controller.messageManager.on('updateBadge', updateBadge) + + // plugin badge text + function updateBadge () { + var label = '' + var unapprovedTxCount = controller.txManager.unapprovedTxCount + var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount + var count = unapprovedTxCount + unapprovedMsgCount + if (count) { + label = String(count) + } + extension.browserAction.setBadgeText({ text: label }) + extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' }) + } -function loadData () { - var oldData = getOldStyleData() - var newData - try { - newData = JSON.parse(window.localStorage[STORAGE_KEY]) - } catch (e) {} + return Promise.resolve() - var data = extend({ - meta: { - version: 0, - }, - data: { - config: { - provider: { - type: 'testnet', - }, - }, - }, - }, oldData || null, newData || null) - return data } -function getOldStyleData () { - var config, wallet, seedWords - - var result = { - meta: { version: 0 }, - data: {}, - } +// +// Etc... +// - try { - config = JSON.parse(window.localStorage['config']) - result.data.config = config - } catch (e) {} - try { - wallet = JSON.parse(window.localStorage['lightwallet']) - result.data.wallet = wallet - } catch (e) {} - try { - seedWords = window.localStorage['seedWords'] - result.data.seedWords = seedWords - } catch (e) {} - - return result +// popup trigger +function triggerUi () { + if (!popupIsOpen) notification.show() } -function setData (data) { - window.localStorage[STORAGE_KEY] = JSON.stringify(data) -} +// On first install, open a window to MetaMask website to how-it-works. +extension.runtime.onInstalled.addListener(function (details) { + if ((details.reason === 'install') && (!METAMASK_DEBUG)) { + extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) + } +}) |