Merge branch 'master' into usd-conversion

feature/default_network_editable
Kevin Serrano 8 years ago
commit 7b85802a0f
  1. 1
      .eslintignore
  2. 1
      .eslintrc
  3. 4
      CHANGELOG.md
  4. 7
      app/manifest.json
  5. 7
      app/scripts/background.js
  6. 3
      app/scripts/chromereload.js
  7. 7
      app/scripts/contentscript.js
  8. 12
      app/scripts/inpage.js
  9. 38
      app/scripts/lib/extension-instance.js
  10. 14
      app/scripts/lib/extension.js
  11. 10
      app/scripts/lib/inpage-provider.js
  12. 31
      app/scripts/lib/notifications.js
  13. 7
      app/scripts/metamask-controller.js
  14. 11
      app/scripts/popup.js
  15. 15
      docs/state_dump.md
  16. 39
      test/unit/extension-test.js
  17. 3
      ui/app/components/transaction-list-item.js
  18. 7
      ui/app/info.js

@ -0,0 +1 @@
app/scripts/lib/extension-instance.js

@ -23,7 +23,6 @@
], ],
"globals": { "globals": {
"chrome": true,
"document": false, "document": false,
"navigator": false, "navigator": false,
"web3": true, "web3": true,

@ -2,6 +2,10 @@
## Current Master ## Current Master
- MetaMask now throws descriptive errors when apps try to use synchronous web3 methods.
## 2.6.2 2016-07-20
- Fixed bug that would prevent the plugin from reopening on the first try after receiving a new transaction while locked. - Fixed bug that would prevent the plugin from reopening on the first try after receiving a new transaction while locked.
- Fixed bug that would render 0 ETH as a non-exact amount. - Fixed bug that would render 0 ETH as a non-exact amount.

@ -1,7 +1,7 @@
{ {
"name": "__MSG_appName__", "name": "__MSG_appName__",
"short_name": "Metamask", "short_name": "Metamask",
"version": "2.6.1", "version": "2.6.2",
"manifest_version": 2, "manifest_version": 2,
"description": "__MSG_appDescription__", "description": "__MSG_appDescription__",
"icons": { "icons": {
@ -37,6 +37,11 @@
"all_frames": false "all_frames": false
} }
], ],
"applications": {
"gecko": {
"id": "MOZILLA_EXTENSION_ID"
}
},
"permissions": [ "permissions": [
"notifications", "notifications",
"storage", "storage",

@ -9,6 +9,7 @@ const createMsgNotification = require('./lib/notifications.js').createMsgNotific
const messageManager = require('./lib/message-manager') const messageManager = require('./lib/message-manager')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const MetamaskController = require('./metamask-controller') const MetamaskController = require('./metamask-controller')
const extension = require('./lib/extension')
const STORAGE_KEY = 'metamask-config' const STORAGE_KEY = 'metamask-config'
@ -65,7 +66,7 @@ function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
// connect to other contexts // connect to other contexts
// //
chrome.runtime.onConnect.addListener(connectRemote) extension.runtime.onConnect.addListener(connectRemote)
function connectRemote (remotePort) { function connectRemote (remotePort) {
var isMetaMaskInternalProcess = (remotePort.name === 'popup') var isMetaMaskInternalProcess = (remotePort.name === 'popup')
var portStream = new PortStream(remotePort) var portStream = new PortStream(remotePort)
@ -133,8 +134,8 @@ function updateBadge (state) {
if (count) { if (count) {
label = String(count) label = String(count)
} }
chrome.browserAction.setBadgeText({ text: label }) extension.browserAction.setBadgeText({ text: label })
chrome.browserAction.setBadgeBackgroundColor({ color: '#506F8B' }) extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' })
} }
function loadData () { function loadData () {

@ -25,11 +25,12 @@
// if (e.data) { // if (e.data) {
// var data = JSON.parse(e.data); // var data = JSON.parse(e.data);
// if (data && data.command === 'reload') { // if (data && data.command === 'reload') {
// chrome.runtime.reload(); // extension.runtime.reload();
// } // }
// } // }
// }; // };
const extension = require('./lib/extension')
window.LiveReloadOptions = { host: 'localhost' }; window.LiveReloadOptions = { host: 'localhost' };
(function e (t, n, r) { function s (o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require === 'function' && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = 'MODULE_NOT_FOUND', f } var l = n[o] = {exports: {}}; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require === 'function' && require; for (var o = 0; o < r.length; o++)s(r[o]); return s })({1: [function (require, module, exports) { (function e (t, n, r) { function s (o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require === 'function' && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = 'MODULE_NOT_FOUND', f } var l = n[o] = {exports: {}}; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require === 'function' && require; for (var o = 0; o < r.length; o++)s(r[o]); return s })({1: [function (require, module, exports) {

@ -1,6 +1,7 @@
const LocalMessageDuplexStream = require('./lib/local-message-stream.js') const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
const PortStream = require('./lib/port-stream.js') const PortStream = require('./lib/port-stream.js')
const ObjectMultiplex = require('./lib/obj-multiplex') const ObjectMultiplex = require('./lib/obj-multiplex')
const extension = require('./lib/extension')
if (shouldInjectWeb3()) { if (shouldInjectWeb3()) {
setupInjection() setupInjection()
@ -10,7 +11,7 @@ if (shouldInjectWeb3()) {
function setupInjection(){ function setupInjection(){
// inject in-page script // inject in-page script
var scriptTag = document.createElement('script') var scriptTag = document.createElement('script')
scriptTag.src = chrome.extension.getURL('scripts/inpage.js') scriptTag.src = extension.extension.getURL('scripts/inpage.js')
scriptTag.onload = function () { this.parentNode.removeChild(this) } scriptTag.onload = function () { this.parentNode.removeChild(this) }
var container = document.head || document.documentElement var container = document.head || document.documentElement
// append as first child // append as first child
@ -25,7 +26,7 @@ function setupStreams(){
target: 'inpage', target: 'inpage',
}) })
pageStream.on('error', console.error.bind(console)) pageStream.on('error', console.error.bind(console))
var pluginPort = chrome.runtime.connect({name: 'contentscript'}) var pluginPort = extension.runtime.connect({name: 'contentscript'})
var pluginStream = new PortStream(pluginPort) var pluginStream = new PortStream(pluginPort)
pluginStream.on('error', console.error.bind(console)) pluginStream.on('error', console.error.bind(console))
@ -49,4 +50,4 @@ function setupStreams(){
function shouldInjectWeb3(){ function shouldInjectWeb3(){
var shouldInject = (window.location.href.indexOf('.pdf') === -1) var shouldInject = (window.location.href.indexOf('.pdf') === -1)
return shouldInject return shouldInject
} }

@ -53,9 +53,17 @@ var __define
function cleanContextForImports () { function cleanContextForImports () {
__define = global.define __define = global.define
delete global.define try {
delete global.define
} catch (_) {
console.warn('MetaMask - global.define could not be deleted.')
}
} }
function restoreContextAfterImports () { function restoreContextAfterImports () {
global.define = __define try {
global.define = __define
} catch (_) {
console.warn('MetaMask - global.define could not be overwritten.')
}
} }

@ -0,0 +1,38 @@
const apis = [
'alarms',
'bookmarks',
'browserAction',
'commands',
'contextMenus',
'cookies',
'downloads',
'events',
'extension',
'extensionTypes',
'history',
'i18n',
'idle',
'notifications',
'pageAction',
'runtime',
'storage',
'tabs',
'webNavigation',
'webRequest',
'windows',
]
function Extension () {
const _this = this
let global = window
if (window.chrome) {
global = window.chrome
}
apis.forEach(function (api) {
_this[api] = global[api]
})
}
module.exports = Extension

@ -0,0 +1,14 @@
/* Extension.js
*
* A module for unifying browser differences in the WebExtension API.
*
* Initially implemented because Chrome hides all of their WebExtension API
* behind a global `chrome` variable, but we'd like to start grooming
* the code-base for cross-browser extension support.
*
* You can read more about the WebExtension API here:
* https://developer.mozilla.org/en-US/Add-ons/WebExtensions
*/
const Extension = require('./extension-instance')
module.exports = new Extension()

@ -107,7 +107,15 @@ function createSyncProvider (providerConfig) {
syncProviderUrl = MetamaskConfig.network.default syncProviderUrl = MetamaskConfig.network.default
} }
} }
return new HttpProvider(syncProviderUrl)
const provider = new HttpProvider(syncProviderUrl)
// Stubbing out the send method to throw on sync methods:
provider.send = function() {
var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq#all-async---think-of-metamask-as-a-light-client for details.'
throw new Error(message)
}
return provider
} }
function remoteStoreWithLocalStorageCache (storageKey) { function remoteStoreWithLocalStorageCache (storageKey) {

@ -7,6 +7,7 @@ const h = require('react-hyperscript')
const PendingTxDetails = require('../../../ui/app/components/pending-tx-details') const PendingTxDetails = require('../../../ui/app/components/pending-tx-details')
const PendingMsgDetails = require('../../../ui/app/components/pending-msg-details') const PendingMsgDetails = require('../../../ui/app/components/pending-msg-details')
const MetaMaskUiCss = require('../../../ui/css') const MetaMaskUiCss = require('../../../ui/css')
const extension = require('./extension')
var notificationHandlers = {} var notificationHandlers = {}
const notifications = { const notifications = {
@ -20,34 +21,34 @@ window.METAMASK_NOTIFIER = notifications
setupListeners() setupListeners()
function setupListeners () { function setupListeners () {
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236 // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
if (!chrome.notifications) return console.error('Chrome notifications API missing...') if (!extension.notifications) return console.error('Chrome notifications API missing...')
// notification button press // notification button press
chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) { extension.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
var handlers = notificationHandlers[notificationId] var handlers = notificationHandlers[notificationId]
if (buttonIndex === 0) { if (buttonIndex === 0) {
handlers.confirm() handlers.confirm()
} else { } else {
handlers.cancel() handlers.cancel()
} }
chrome.notifications.clear(notificationId) extension.notifications.clear(notificationId)
}) })
// notification teardown // notification teardown
chrome.notifications.onClosed.addListener(function (notificationId) { extension.notifications.onClosed.addListener(function (notificationId) {
delete notificationHandlers[notificationId] delete notificationHandlers[notificationId]
}) })
} }
// creation helper // creation helper
function createUnlockRequestNotification (opts) { function createUnlockRequestNotification (opts) {
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236 // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
if (!chrome.notifications) return console.error('Chrome notifications API missing...') if (!extension.notifications) return console.error('Chrome notifications API missing...')
var message = 'An Ethereum app has requested a signature. Please unlock your account.' var message = 'An Ethereum app has requested a signature. Please unlock your account.'
var id = createId() var id = createId()
chrome.notifications.create(id, { extension.notifications.create(id, {
type: 'basic', type: 'basic',
iconUrl: '/images/icon-128.png', iconUrl: '/images/icon-128.png',
title: opts.title, title: opts.title,
@ -56,8 +57,8 @@ function createUnlockRequestNotification (opts) {
} }
function createTxNotification (state) { function createTxNotification (state) {
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236 // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
if (!chrome.notifications) return console.error('Chrome notifications API missing...') if (!extension.notifications) return console.error('Chrome notifications API missing...')
renderTxNotificationSVG(state, function (err, notificationSvgSource) { renderTxNotificationSVG(state, function (err, notificationSvgSource) {
if (err) throw err if (err) throw err
@ -70,8 +71,8 @@ function createTxNotification (state) {
} }
function createMsgNotification (state) { function createMsgNotification (state) {
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236 // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
if (!chrome.notifications) return console.error('Chrome notifications API missing...') if (!extension.notifications) return console.error('Chrome notifications API missing...')
renderMsgNotificationSVG(state, function (err, notificationSvgSource) { renderMsgNotificationSVG(state, function (err, notificationSvgSource) {
if (err) throw err if (err) throw err
@ -84,11 +85,11 @@ function createMsgNotification (state) {
} }
function showNotification (state) { function showNotification (state) {
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236 // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
if (!chrome.notifications) return console.error('Chrome notifications API missing...') if (!extension.notifications) return console.error('Chrome notifications API missing...')
var id = createId() var id = createId()
chrome.notifications.create(id, { extension.notifications.create(id, {
type: 'image', type: 'image',
requireInteraction: true, requireInteraction: true,
iconUrl: '/images/icon-128.png', iconUrl: '/images/icon-128.png',

@ -6,6 +6,7 @@ const messageManager = require('./lib/message-manager')
const HostStore = require('./lib/remote-store.js').HostStore const HostStore = require('./lib/remote-store.js').HostStore
const Web3 = require('web3') const Web3 = require('web3')
const ConfigManager = require('./lib/config-manager') const ConfigManager = require('./lib/config-manager')
const extension = require('./lib/extension')
module.exports = class MetamaskController { module.exports = class MetamaskController {
@ -254,19 +255,19 @@ module.exports = class MetamaskController {
// called from popup // called from popup
setRpcTarget (rpcTarget) { setRpcTarget (rpcTarget) {
this.configManager.setRpcTarget(rpcTarget) this.configManager.setRpcTarget(rpcTarget)
chrome.runtime.reload() extension.runtime.reload()
this.idStore.getNetwork() this.idStore.getNetwork()
} }
setProviderType (type) { setProviderType (type) {
this.configManager.setProviderType(type) this.configManager.setProviderType(type)
chrome.runtime.reload() extension.runtime.reload()
this.idStore.getNetwork() this.idStore.getNetwork()
} }
useEtherscanProvider () { useEtherscanProvider () {
this.configManager.useEtherscanProvider() this.configManager.useEtherscanProvider()
chrome.runtime.reload() extension.runtime.reload()
} }
} }

@ -9,6 +9,7 @@ const injectCss = require('inject-css')
const PortStream = require('./lib/port-stream.js') const PortStream = require('./lib/port-stream.js')
const StreamProvider = require('web3-stream-provider') const StreamProvider = require('web3-stream-provider')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const extension = require('./lib/extension')
// setup app // setup app
var css = MetaMaskUiCss() var css = MetaMaskUiCss()
@ -21,7 +22,7 @@ async.parallel({
function connectToAccountManager (cb) { function connectToAccountManager (cb) {
// setup communication with background // setup communication with background
var pluginPort = chrome.runtime.connect({name: 'popup'}) var pluginPort = extension.runtime.connect({name: 'popup'})
var portStream = new PortStream(pluginPort) var portStream = new PortStream(pluginPort)
// setup multiplexing // setup multiplexing
var mx = setupMultiplex(portStream) var mx = setupMultiplex(portStream)
@ -55,8 +56,8 @@ function setupControllerConnection (stream, cb) {
function getCurrentDomain (cb) { function getCurrentDomain (cb) {
const unknown = '<unknown>' const unknown = '<unknown>'
if (!chrome.tabs) return cb(null, unknown) if (!extension.tabs) return cb(null, unknown)
chrome.tabs.query({active: true, currentWindow: true}, function (results) { extension.tabs.query({active: true, currentWindow: true}, function (results) {
var activeTab = results[0] var activeTab = results[0]
var currentUrl = activeTab && activeTab.url var currentUrl = activeTab && activeTab.url
var currentDomain = url.parse(currentUrl).host var currentDomain = url.parse(currentUrl).host
@ -68,9 +69,9 @@ function getCurrentDomain (cb) {
} }
function clearNotifications(){ function clearNotifications(){
chrome.notifications.getAll(function (object) { extension.notifications.getAll(function (object) {
for (let notification in object){ for (let notification in object){
chrome.notifications.clear(notification) extension.notifications.clear(notification)
} }
}) })
} }

@ -0,0 +1,15 @@
# How to take a State Dump
Sometimes a UI bug is hard to reproduce, but we'd like to rapidly develop against the application state that caused the bug.
In this case, a MetaMask developer will sometimes ask a user with a bug to perform a "state dump", so we can use some internal tools to reproduce and fix the bug.
To take a state dump, follow these steps:
1. Get the MetaMask popup to the point where it shows the bug (the developer will probably specify exactly where).
2. Right click on the extension popup UI, and in the menu, click "Inspect". This will open the developer tools.
3. In case it isn't already selected, click the "Console" tab in the new Developer Tools window.
4. In the console, type this command exactly: `logState()`. This should print a bunch of JSON text into your console.
5. Copy that printed JSON text
6. *Optional*: Annonymize that text if you'd like (you may change all instances of an account address to another valid account address, for example) We may automate the anonymization in the future.
7. Send that JSON text to the developer, ideally pasting it in the issue regarding the bug.

@ -0,0 +1,39 @@
var assert = require('assert')
var sinon = require('sinon')
const ethUtil = require('ethereumjs-util')
var path = require('path')
var Extension = require(path.join(__dirname, '..', '..', 'app', 'scripts', 'lib', 'extension-instance.js'))
describe('extension', function() {
describe('with chrome global', function() {
let extension
beforeEach(function() {
window.chrome = {
alarms: 'foo'
}
extension = new Extension()
})
it('should use the chrome global apis', function() {
assert.equal(extension.alarms, 'foo')
})
})
describe('without chrome global', function() {
let extension
beforeEach(function() {
window.chrome = undefined
window.alarms = 'foo'
extension = new Extension()
})
it('should use the global apis', function() {
assert.equal(extension.alarms, 'foo')
})
})
})

@ -7,6 +7,7 @@ const addressSummary = require('../util').addressSummary
const explorerLink = require('../../lib/explorer-link') const explorerLink = require('../../lib/explorer-link')
const CopyButton = require('./copyButton') const CopyButton = require('./copyButton')
const vreme = new (require('vreme')) const vreme = new (require('vreme'))
const extension = require('../../../app/scripts/lib/extension')
const TransactionIcon = require('./transaction-list-item-icon') const TransactionIcon = require('./transaction-list-item-icon')
@ -49,7 +50,7 @@ TransactionListItem.prototype.render = function () {
if (!transaction.hash || !isLinkable) return if (!transaction.hash || !isLinkable) return
var url = explorerLink(transaction.hash, parseInt(network)) var url = explorerLink(transaction.hash, parseInt(network))
chrome.tabs.create({ url }) extension.tabs.create({ url })
}, },
style: { style: {
padding: '20px 0', padding: '20px 0',

@ -3,6 +3,7 @@ const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const actions = require('./actions') const actions = require('./actions')
const extension = require('../../app/scripts/lib/extension')
module.exports = connect(mapStateToProps)(InfoScreen) module.exports = connect(mapStateToProps)(InfoScreen)
@ -19,7 +20,7 @@ InfoScreen.prototype.render = function () {
var state = this.props var state = this.props
var manifest var manifest
try { try {
manifest = chrome.runtime.getManifest() manifest = extension.runtime.getManifest()
} catch (e) { } catch (e) {
manifest = { version: '2.0.0' } manifest = { version: '2.0.0' }
} }
@ -105,7 +106,7 @@ InfoScreen.prototype.render = function () {
h('a.info', { h('a.info', {
target: '_blank', target: '_blank',
style: { width: '85vw' }, style: { width: '85vw' },
onClick () { chrome.tabs.create({url: 'mailto:help@metamask.io?subject=Feedback'}) }, onClick () { extension.tabs.create({url: 'mailto:help@metamask.io?subject=Feedback'}) },
}, 'Email us any questions or comments!'), }, 'Email us any questions or comments!'),
]), ]),
@ -124,5 +125,5 @@ InfoScreen.prototype.render = function () {
} }
InfoScreen.prototype.navigateTo = function (url) { InfoScreen.prototype.navigateTo = function (url) {
chrome.tabs.create({ url }) extension.tabs.create({ url })
} }

Loading…
Cancel
Save