Skip to content
This repository has been archived by the owner on Mar 25, 2019. It is now read-only.

Rough version of web3 support and embeddable ParityBar #16

Merged
merged 21 commits into from
Jan 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 172 additions & 5 deletions background/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,180 @@
/* global chrome */

import Processor from './processor';
import Ws from './ws';

const processor = new Processor();
import { UI, TRANSPORT_UNINITIALIZED, getRetryTimeout } from '../shared';

chrome.runtime.onConnect.addListener((port) => {
console.assert(port.name === 'id');
if (port.name === 'secureApi') {
port.onMessage.addListener(web3Message(port));
return;
}

if (port.name === 'barScripts') {
port.onMessage.addListener(loadScripts(port));
return;
}

// TODO [ToDr] Use connection without elevated privileges here!
if (port.name === 'web3') {
port.onMessage.addListener(web3Message(port));
return;
}

if (port.name === 'id') {
port.onMessage.addListener(processId(port));
return;
}

throw new Error(`Unrecognized port: ${port.name}`);
});

let openedTabId = null;
let transport = null;
// Attempt to extract token on start if not available.
extractToken();

chrome.runtime.onMessage.addListener((request, sender, callback) => {
if (!(transport && transport.isConnected) && request.token) {
if (transport) {
// TODO [ToDr] kill old transport!
}

if (openedTabId) {
chrome.tabs.remove(openedTabId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't it looks a bit sketchy to randomly open and close the tab when the Extension installs ? The user won't have the time to understand what happens ; I know I would be surprised. Might be better to have a settings/options/status window where it says that it needs the auth token, and on a button click it could open the right tab and gather the right data. However this might be included in a future issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That tab is opened in the background (and it always opens only once), we also check if the node is up before opening it.

I think it might be better to just display a welcome page (that's actually quite a common thing for extensions) loaded from http://127.0.0.1:8180/#/welcome - it could list extension features, and the token would be extracted automatically :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with the welcome message, as per other extensions. Would log it in another issue though.
Because even if the tab is open in background, you can still see it open and close, right?

openedTabId = null;
}

console.log('Extracted a token: ', request.token);
console.log('Extracted backgroundSeed: ', request.backgroundSeed);
chrome.storage.local.set({
'authToken': request.token,
'backgroundSeed': request.backgroundSeed
}, () => {});
transport = new Ws(`ws://${UI}`, request.token, true);
return;
}
});

let codeCache = null;
function loadScripts (port) {
function retry (msg) {
if (msg.type !== 'parity.bar.code') {
return;
}

if (!codeCache) {
const vendor = fetch(`http://${UI}/vendor.js`)
.then(x => x.blob());
const embed = fetch(`http://${UI}/embed.html`)
.then(x => x.text())
.then(page => ({
styles: /styles\/embed\.([a-z0-9]{10})\.css/.exec(page),
scripts: /embed\.([a-z0-9]{10})\.js/.exec(page)
}))
.then(res => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to use a ServiceWorker here. It's its job to fetch and cache requests, and it's work pretty good. Also it might be good to have a way to export which files are required by the embed.html file, instead of using some RegExp. There might be a Webpack plugin/option for that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT you cannot use ServiceWorker in background script of chrome extension (cause actually background scripts are more powerful then service workers). Also we cannot use service worker in the iframe, because of Mixed Content limitations.

So the way it works is:
Load an iframe with chrome-extension://<hash>/web3/embed.html and that page asks a background thread to fetch and provide correct files. (As I said ServiceWorker is not an option, cause the browser would detect that https had iframe that fetched non-https content and would block it as violating non-secure source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, didn't know... So the API is not available either ?

return Promise.all([
fetch(`http://${UI}/${res.styles[0]}`),
fetch(`http://${UI}/${res.scripts[0]}`)
]);
})
.then(x => Promise.all(x.map(x => x.blob())));

codeCache = Promise.all([vendor, embed])
.then(scripts => {
const vendor = scripts[0];
const styles = scripts[1][0];
const embed = scripts[1][1];
// Concat blobs
const blob = new Blob([vendor, embed], { type: 'application/javascript' });
return {
styles: URL.createObjectURL(styles),
scripts: URL.createObjectURL(blob)
};
});
}

codeCache
.then(code => {
retry.retries = 0;
port.postMessage(code);
})
.catch(err => {
codeCache = null;
retry.retries += 1;

console.error('Could not load ParityBar scripts. Retrying in a while..', err);
setTimeout(() => retry(msg), getRetryTimeout(retry.retries));
});
}

retry.retries = 0;
return retry;
}

function extractToken () {
chrome.storage.local.get('authToken', (token) => {
if (!token.authToken) {
fetch(`http://${UI}`)
.then(() => {
// Open a UI to extract the token from it
chrome.tabs.create({
url: `http://${UI}`,
active: false
}, (tab) => {
openedTabId = tab.id;
});
extractToken.retries = 0;
})
.catch(err => {
console.error('Node seems down, will re-try', err);
extractToken.retries += 1;
setTimeout(() => extractToken(), getRetryTimeout(extractToken.retries));
});
return;
}

transport = new Ws(`ws://${UI}`, token.authToken, true);
});
}
extractToken.retries = 0;

function web3Message (port) {
return (msg) => {
const { id, payload } = msg;
if (!transport || !transport.isConnected) {
console.error('Transport uninitialized!');
port.postMessage({
id, err: TRANSPORT_UNINITIALIZED,
payload: null,
connected: false
});
return;
}

transport.executeRaw(payload)
.then((response) => {
port.postMessage({
id,
err: null,
payload: response,
connected: true
});
})
.catch((err) => {
port.postMessage({
id,
err,
payload: null
});
});
};
}

port.onMessage.addListener((message = {}) => {
const processor = new Processor();
function processId (port) {
return (message = {}) => {
const { id, data } = message;

processor
Expand All @@ -40,5 +207,5 @@ chrome.runtime.onConnect.addListener((port) => {

throw error;
});
});
});
};
}
Loading