diff options
Diffstat (limited to 'library')
-rw-r--r-- | library/controller.js | 22 | ||||
-rw-r--r-- | library/controllers/index-db-controller.js | 88 | ||||
-rw-r--r-- | library/example/index.js | 4 | ||||
-rw-r--r-- | library/index.js | 4 | ||||
-rw-r--r-- | library/lib/setup-provider.js | 1 | ||||
-rw-r--r-- | library/lib/setup-untrusted-connection.js | 27 | ||||
-rw-r--r-- | library/popup.js | 20 | ||||
-rw-r--r-- | library/server.js | 6 | ||||
-rw-r--r-- | library/sw-controller.js | 64 | ||||
-rw-r--r-- | library/sw-core.js | 163 |
10 files changed, 392 insertions, 7 deletions
diff --git a/library/controller.js b/library/controller.js index 5823287cc..cbba9584a 100644 --- a/library/controller.js +++ b/library/controller.js @@ -1,3 +1,23 @@ +const ParentStream = require('iframe-stream').ParentStream +const SWcontroller = require('./sw-controller') +const SwStream = require('sw-stream/lib/sw-stream.js') +const SetupUntrustedComunication = ('./lib/setup-untrusted-connection.js') +const background = new SWcontroller({ + fileName: '/popup/sw-build.js', +}) + +background.on('ready', (readSw) => { + // var inpageProvider = new MetamaskInpageProvider(SwStream(background.controller)) + let pageStream = new ParentStream() + let swStream = SwStream(background.controller) + pageStream.pipe(swStream).pipe(pageStream) +}) + +background.on('error', console.error) +background.startWorker() + +console.log('hello from controller') +/* const urlUtil = require('url') const extend = require('xtend') const Dnode = require('dnode') @@ -157,3 +177,5 @@ function initializeZeroClient() { } } + +*/ diff --git a/library/controllers/index-db-controller.js b/library/controllers/index-db-controller.js new file mode 100644 index 000000000..041ddae2e --- /dev/null +++ b/library/controllers/index-db-controller.js @@ -0,0 +1,88 @@ +const EventEmitter = require('events') +module.exports = class IndexDbController extends EventEmitter { + + constructor (opts) { + super() + this.migrations = opts.migrations + this.key = opts.key + this.dbObject = global.indexedDB + this.IDBTransaction = global.IDBTransaction || global.webkitIDBTransaction || global.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browsers + this.IDBKeyRange = global.IDBKeyRange || global.webkitIDBKeyRange || global.msIDBKeyRange; + this.version = opts.version + this.logging = opts.logging + this.initialState = opts.initialState + if (this.logging) this.on('log', logger) + } + + // Opens the database connection and returns a promise + open (version = this.version) { + return new Promise((resolve, reject) => { + const dbOpenRequest = this.dbObject.open(this.key, version) + dbOpenRequest.onerror = (event) => { + return reject(event) + } + dbOpenRequest.onsuccess = (event) => { + this.db = dbOpenRequest.result + this.emit('success') + resolve(this.db) + } + dbOpenRequest.onupgradeneeded = (event) => { + this.db = event.target.result + this.db.createObjectStore('dataStore') + } + }) + .then((openRequest) => { + return this.get('dataStore') + }) + .then((data) => { + if (!data) { + return this._add('dataStore', this.initialState) + .then(() => this.get('dataStore')) + .then((versionedData) => Promise.resolve(versionedData.data)) + } + return Promise.resolve(data) + }) + } + + requestObjectStore (key, type = 'readonly') { + return new Promise((resolve, reject) => { + const dbReadWrite = this.db.transaction(key, type) + const dataStore = dbReadWrite.objectStore(key) + resolve(dataStore) + }) + } + + get (key = 'dataStore') { + return this.requestObjectStore(key) + .then((dataObject)=> { + return new Promise((resolve, reject) => { + const getRequest = dataObject.get(key) + getRequest.onsuccess = (event) => resolve(event.currentTarget.result) + getRequest.onerror = (event) => reject(event) + }) + }) + } + + put (state) { + return this.requestObjectStore('dataStore', 'readwrite') + .then((dataObject)=> { + const putRequest = dataObject.put(state, 'dataStore') + putRequest.onsuccess = (event) => Promise.resolve(event.currentTarget.result) + putRequest.onerror = (event) => Promise.reject(event) + }) + } + + _add (key, objStore, cb = logger) { + return this.requestObjectStore(key, 'readwrite') + .then((dataObject)=> { + const addRequest = dataObject.add(objStore, key) + addRequest.onsuccess = (event) => Promise.resolve(event.currentTarget.result) + addRequest.onerror = (event) => Promise.reject(event) + }) + } + +} + +function logger (err, ress) { + err ? console.error(`Logger says: ${err}`) : console.dir(`Logger says: ${ress}`) +} diff --git a/library/example/index.js b/library/example/index.js index 4a107df6a..329302a4d 100644 --- a/library/example/index.js +++ b/library/example/index.js @@ -1,4 +1,3 @@ - window.addEventListener('load', web3Detect) function web3Detect() { @@ -18,6 +17,7 @@ function startApp(){ web3.eth.getAccounts(function(err, addresses){ if (err) throw err console.log('set address', addresses[0]) + debugger primaryAccount = addresses[0] }) @@ -53,4 +53,4 @@ function startApp(){ function logToDom(message){ document.body.appendChild(document.createTextNode(message)) console.log(message) -}
\ No newline at end of file +} diff --git a/library/index.js b/library/index.js index b5f4f6637..44ee401d8 100644 --- a/library/index.js +++ b/library/index.js @@ -26,7 +26,7 @@ var shouldPop = false window.addEventListener('click', function(){ if (!shouldPop) return shouldPop = false - window.open('http://127.0.0.1:9001/popup/popup.html', '', 'width=360 height=500') + window.open('http://localhost:9001/popup/popup.html', '', 'width=360 height=500') console.log('opening window...') }) @@ -41,3 +41,5 @@ function hijackProvider(provider){ _super(payload, cb) } } + + diff --git a/library/lib/setup-provider.js b/library/lib/setup-provider.js index 9efd209cb..68be99c9e 100644 --- a/library/lib/setup-provider.js +++ b/library/lib/setup-provider.js @@ -22,4 +22,3 @@ function getProvider(){ return inpageProvider } - diff --git a/library/lib/setup-untrusted-connection.js b/library/lib/setup-untrusted-connection.js new file mode 100644 index 000000000..b2aeb7905 --- /dev/null +++ b/library/lib/setup-untrusted-connection.js @@ -0,0 +1,27 @@ + +/* +IFRAME + var pageStream = new LocalMessageDuplexStream({ + name: 'contentscript', + target: 'inpage', + }) +SERVICEWORKER + pageStream.on('error', console.error) + var pluginPort = extension.runtime.connect({name: 'contentscript'}) + var pluginStream = new PortStream(pluginPort) + pluginStream.on('error', console.error) +IFRAME --> SW + // forward communication plugin->inpage + pageStream.pipe(pluginStream).pipe(pageStream) +*/ + +module.exports = SetupUntrustedComunicationWithSW + +function SetupUntrustedComunicationWithSW (connectionStream, readySwStream) { + pageStream.on('error', console.error) + var pluginPort = extension.runtime.connect({name: 'contentscript'}) + var pluginStream = new PortStream(pluginPort) + pluginStream.on('error', console.error) + // forward communication plugin->inpage + pageStream.pipe(pluginStream).pipe(pageStream) +} diff --git a/library/popup.js b/library/popup.js index 667b13371..d956dc0b1 100644 --- a/library/popup.js +++ b/library/popup.js @@ -1,7 +1,10 @@ const injectCss = require('inject-css') const MetaMaskUiCss = require('../ui/css') -const startPopup = require('../app/scripts/popup-core') const setupIframe = require('./lib/setup-iframe.js') +const MetamaskInpageProvider = require('../app/scripts/lib/inpage-provider.js') +const SWcontroller = require('./sw-controller') +const SwStream = require('sw-stream/lib/sw-stream.js') +const startPopup = require('../app/scripts/popup-core') var css = MetaMaskUiCss() @@ -11,9 +14,20 @@ var name = 'popup' window.METAMASK_UI_TYPE = name var iframeStream = setupIframe({ - zeroClientProvider: 'http://127.0.0.1:9001', + zeroClientProvider: 'http://localhost:9001', sandboxAttributes: ['allow-scripts', 'allow-popups', 'allow-same-origin'], container: document.body, }) +console.log('outside:open') -startPopup(iframeStream) +const background = new SWcontroller({ + fileName: '/popup/sw-build.js', +}) +background.on('ready', (readSw) => { + // var inpageProvider = new MetamaskInpageProvider(SwStream(background.controller)) + // startPopup(inpageProvider) + startPopup(SwStream(background.controller)) +}) +background.on('message', (messageEvent) => {debugger}) +background.startWorker() +console.log('hello from /library/popup.js') diff --git a/library/server.js b/library/server.js index 797ad8a77..a284d3e05 100644 --- a/library/server.js +++ b/library/server.js @@ -7,6 +7,7 @@ const zeroBundle = createBundle('./index.js') const controllerBundle = createBundle('./controller.js') const popupBundle = createBundle('./popup.js') const appBundle = createBundle('./example/index.js') +const swBuild = createBundle('./sw-core.js') // // Iframe Server @@ -24,6 +25,11 @@ iframeServer.use('/popup', express.static('../dist/chrome')) iframeServer.get('/controller.js', function(req, res){ res.send(controllerBundle.latest) }) +iframeServer.get('/popup/sw-build.js', function(req, res){ + console.log('/sw-build.js') + res.setHeader('Content-Type', 'application/javascript') + res.send(swBuild.latest) +}) // serve background controller iframeServer.use(express.static('./server')) diff --git a/library/sw-controller.js b/library/sw-controller.js new file mode 100644 index 000000000..1a7b1cad3 --- /dev/null +++ b/library/sw-controller.js @@ -0,0 +1,64 @@ +const EventEmitter = require('events') + +module.exports = class ClientSideServiceWorker extends EventEmitter{ + constructor (opts) { + super() + this.fileName = opts.fileName + this.startDelay = opts.startDelay + + this.serviceWorkerApi = navigator.serviceWorker + this.serviceWorkerApi.onmessage = (messageEvent) => this.emit('message', messageEvent) + this.serviceWorkerApi.onerror = (err) => this.emit('error', err) + this.on('message', (messageEvent) => {debugger}) + if (opts.initStart) this.startWorker() + } + + get controller () { + return this.sw || this.serviceWorkerApi.controller + } + + + startWorker () { + return this.registerWorker() + .then((sw) => { + this.sw = sw + this.sw.onerror = (err) => this.emit('error', err) + this.sw = sw + this.emit('ready', this.sw) + }) + .catch((err) => this.emit('error', err)) + } + + registerWorker () { + return this.serviceWorkerApi.register(this.fileName) + .then((registerdWorker) => { + return new Promise((resolve, reject) => { + let timeOutId = setTimeout(() => { + if (this.serviceWorkerApi.controller) return resolve(this.serviceWorkerApi.controller) + if (registerdWorker.active) return resolve(registerdWorker.active) + return reject(new Error('ClientSideServiceWorker: No controller found and onupdatefound timed out')) + }, this.startDelay || 1000 ) + + registerdWorker.onupdatefound = (event) => { + this.emit('updatefound') + registerdWorker.update() + } + }) + }) + } + + sendMessage (message) { + const self = this + return new Promise((resolve, reject) => { + var messageChannel = new MessageChannel() + messageChannel.port1.onmessage = (event) => { + if (event.data.err) { + reject(event.data.error) + } else { + resolve(event.data.data) + } + } + this.controller.postMessage(message, [messageChannel.port2]) + }) + } +} diff --git a/library/sw-core.js b/library/sw-core.js new file mode 100644 index 000000000..1d31b2acd --- /dev/null +++ b/library/sw-core.js @@ -0,0 +1,163 @@ +global.window = global +const asyncQ = require('async-q') +const pipe = require('pump') + +const SwGlobalListener = require('sw-stream/lib/sw-global-listener.js') +const connectionListener = new SwGlobalListener(self) +const setupMultiplex = require('../app/scripts/lib/stream-utils.js').setupMultiplex +const PortStream = require('../app/scripts/lib/port-stream.js') +// const notification = require('../app/scripts/lib/notifications.js') + +const DbController = require('./controllers/index-db-controller') + +const MetamaskController = require('../app/scripts/metamask-controller') +const extension = {} //require('../app/scripts/lib/extension') +// const LocalStorageStore = require('obs-store/lib/localStorage') +const storeTransform = require('obs-store/lib/transform') +const Migrator = require('../app/scripts/lib/migrator/') +const migrations = require('../app/scripts/migrations/') +const firstTimeState = require('../app/scripts/first-time-state') + +const STORAGE_KEY = 'metamask-config' +const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' +let popupIsOpen = false + +const log = require('loglevel') +global.log = log +log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn') + +self.addEventListener('install', function(event) { + event.waitUntil(self.skipWaiting()) +}) +self.addEventListener('activate', function(event) { + event.waitUntil(self.clients.claim()) +}) + +console.log('inside:open') + + +// // state persistence +let diskStore +const dbController = new DbController({ + key: STORAGE_KEY, + global: self, + version: 2, +}) +asyncQ.waterfall([ + () => loadStateFromPersistence(), + (initState) => setupController(initState), +]) +.then(() => console.log('MetaMask initialization complete.')) +.catch((err) => { + console.log('WHILE SETTING UP:') + console.error(err) +}) + +// initialization flow + +// +// State and Persistence +// +function loadStateFromPersistence() { + // migrations + let migrator = new Migrator({ migrations }) + const initialState = migrator.generateInitialState(firstTimeState) + dbController.initialState = initialState + return dbController.open() + .then((versionedData) => migrator.migrateData(versionedData)) + .then((versionedData) => { + dbController.put(versionedData) + return Promise.resolve(versionedData) + }) + .then((versionedData) => Promise.resolve(versionedData.data)) +} + +function setupController (initState, client) { + + // + // MetaMask Controller + // + + const controller = new MetamaskController({ + // User confirmation callbacks: + showUnconfirmedMessage: triggerUi, + unlockAccountMessage: triggerUi, + showUnapprovedTx: triggerUi, + // initial state + initState, + }) + global.metamaskController = controller + + // setup state persistence + // pipe( + // controller.store, + // storeTransform(versionifyData), + // diskStore + // ) + controller.store.subscribe((state) => { + versionifyData(state) + .then((versionedData) => dbController.put(versionedData)) + .catch((err) => {console.error(err)}) + }) + function versionifyData(state) { + return dbController.get() + .then((rawData) => { + return Promise.resolve({ + data: state, + meta: rawData.meta, + })} + ) + } + + // + // connect to other contexts + // + /* + need to write a service worker stream for this + */ + connectionListener.on('remote', (portStream, messageEvent) => { + connectRemote(portStream, messageEvent.origin) + }) + + function connectRemote (connectionStream, originDomain) { + var isMetaMaskInternalProcess = (originDomain === 'http://localhost:9001') + if (isMetaMaskInternalProcess) { + // communication with popup + controller.setupTrustedCommunication(connectionStream, 'MetaMask') + popupIsOpen = true + } else { + // communication with page + setupUntrustedCommunication(connectionStream, 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 setupTrustedCommunication (connectionStream, originDomain) { + // setup multiplexing + var mx = setupMultiplex(connectionStream) + // connect features + controller.setupProviderConnection(mx.createStream('provider'), originDomain) + } + // + // User Interface setup + // + return Promise.resolve() + +} + +// // // +// // // Etc... +// // // + +// // // popup trigger + +/*send a message to the client that has focus and tell it to open a window*/ +function triggerUi () { +} |