diff --git a/.eslintrc b/.eslintrc index d7cb8022e..7fab8f127 100644 --- a/.eslintrc +++ b/.eslintrc @@ -98,7 +98,7 @@ "no-obj-calls": 2, "no-octal": 2, "no-octal-escape": 2, - "no-path-concat": 2, + "no-path-concat": 1, "no-proto": 2, "no-redeclare": 2, "no-regex-spaces": 2, diff --git a/CHANGELOG.md b/CHANGELOG.md index d9aee6ac1..a0a4b8664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Fix bug where web3 would sometimes not be injected in time for the application. - Added a Warning screen about storing ETH - Add buy Button! - MetaMask now throws descriptive errors when apps try to use synchronous web3 methods. diff --git a/README.md b/README.md index 34fac137c..59d52b6f1 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,20 @@ Click `Select`. You now have the plugin, and can click 'inspect views: background plugin' to view its dev console. +#### In Firefox (Developer Edition Only) + +Go to the url `about:debugging`. + +Click the button `Load Temporary Add-On`. + +Select the file `dist/manifest.json`. + +You can optionally enable debugging, and click `Debug`, for a console window that logs all of Metamask's processes to a single console. + +If you have problems debugging, try connecting to the IRC channel `#webextensions` on `irc.mozilla.org`. + +For longer questions, use the StackOverfow tag `firefox-addons`. + ### Developing on UI Only You can run `npm run ui`, and your browser should open a live-reloading demo version of the plugin UI. diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 0ffe93e3c..1eb04059d 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -3,19 +3,37 @@ const PortStream = require('./lib/port-stream.js') const ObjectMultiplex = require('./lib/obj-multiplex') const extension = require('./lib/extension') +const fs = require('fs') +const path = require('path') +const inpageText = fs.readFileSync(path.join(__dirname + '/inpage.js')).toString() + +// Eventually this streaming injection could be replaced with: +// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction +// +// But for now that is only Firefox +// If we create a FireFox-only code path using that API, +// MetaMask will be much faster loading and performant on Firefox. + if (shouldInjectWeb3()) { setupInjection() setupStreams() } function setupInjection(){ - // inject in-page script - var scriptTag = document.createElement('script') - scriptTag.src = extension.extension.getURL('scripts/inpage.js') - scriptTag.onload = function () { this.parentNode.removeChild(this) } - var container = document.head || document.documentElement - // append as first child - container.insertBefore(scriptTag, container.children[0]) + try { + + // inject in-page script + var scriptTag = document.createElement('script') + scriptTag.src = extension.extension.getURL('scripts/inpage.js') + scriptTag.textContent = inpageText + scriptTag.onload = function () { this.parentNode.removeChild(this) } + var container = document.head || document.documentElement + // append as first child + container.insertBefore(scriptTag, container.children[0]) + + } catch (e) { + console.error('Metamask injection failed.', e) + } } function setupStreams(){ @@ -44,7 +62,6 @@ function setupStreams(){ pluginStream.on('close', function () { reloadStream.write({ method: 'reset' }) }) - } function shouldInjectWeb3(){ diff --git a/app/scripts/lib/extension-instance.js b/app/scripts/lib/extension-instance.js index eeab6c6d0..eb3b8a1e9 100644 --- a/app/scripts/lib/extension-instance.js +++ b/app/scripts/lib/extension-instance.js @@ -24,14 +24,27 @@ const apis = [ function Extension () { const _this = this - let global = window - - if (window.chrome) { - global = window.chrome - } apis.forEach(function (api) { - _this[api] = global[api] + + _this[api] = null + + try { + if (chrome[api]) { + _this[api] = chrome[api] + } + } catch (e) {} + + try { + if (window[api]) { + _this[api] = window[api] + } + } catch (e) {} + + try { + _this.api = browser.extension[api] + } catch (e) {} + }) } diff --git a/gulpfile.js b/gulpfile.js index 941155ff4..e10a4eb7d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,6 +8,7 @@ var watch = require('gulp-watch') var sourcemaps = require('gulp-sourcemaps') var assign = require('lodash.assign') var livereload = require('gulp-livereload') +var brfs = require('gulp-brfs') var del = require('del') var eslint = require('gulp-eslint') var fs = require('fs') @@ -144,6 +145,7 @@ function bundleTask(opts) { // log errors if they happen .on('error', gutil.log.bind(gutil, 'Browserify Error')) .pipe(source(opts.filename)) + .pipe(brfs()) // optional, remove if you don't need to buffer file contents .pipe(buffer()) // optional, remove if you dont want sourcemaps diff --git a/package.json b/package.json index ef570fb94..2c264cfd0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "private": true, "scripts": { "start": "gulp dev", - "test": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\" && npm run ci", + "test": "npm run fastTest && npm run ci", + "fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"", "watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"", "ui": "node development/genStates.js && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", "mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", @@ -86,6 +87,7 @@ "deep-freeze-strict": "^1.1.1", "del": "^2.2.0", "gulp": "github:gulpjs/gulp#4.0", + "gulp-brfs": "^0.1.0", "gulp-livereload": "^3.8.1", "gulp-sourcemaps": "^1.6.0", "gulp-util": "^3.0.7", diff --git a/test/unit/extension-test.js b/test/unit/extension-test.js index 0a03a3b97..6b695e835 100644 --- a/test/unit/extension-test.js +++ b/test/unit/extension-test.js @@ -1,6 +1,8 @@ var assert = require('assert') var sinon = require('sinon') const ethUtil = require('ethereumjs-util') +GLOBAL.chrome = {} +GLOBAL.browser = {} var path = require('path') var Extension = require(path.join(__dirname, '..', '..', 'app', 'scripts', 'lib', 'extension-instance.js')) @@ -11,7 +13,7 @@ describe('extension', function() { let extension beforeEach(function() { - window.chrome = { + GLOBAL.chrome = { alarms: 'foo' } extension = new Extension() @@ -24,13 +26,20 @@ describe('extension', function() { describe('without chrome global', function() { let extension + let realWindow beforeEach(function() { - window.chrome = undefined - window.alarms = 'foo' + realWindow = window + window = GLOBAL + GLOBAL.chrome = undefined + GLOBAL.alarms = 'foo' extension = new Extension() }) + after(function() { + window = realWindow + }) + it('should use the global apis', function() { assert.equal(extension.alarms, 'foo') })