From 4619405accb9e49683281c1136ea6759b92c343a Mon Sep 17 00:00:00 2001 From: DC Date: Tue, 8 Nov 2016 17:24:55 -0800 Subject: [PATCH] move webtorrent to the browser process --- app/browser/webtorrent.js | 102 ++++++++++++++++++++++++++++++++++++++ app/index.js | 1 + js/components/frame.js | 21 ++++++++ js/constants/messages.js | 4 +- js/webtorrent/entry.js | 84 ++++++++++++++++--------------- 5 files changed, 170 insertions(+), 42 deletions(-) create mode 100644 app/browser/webtorrent.js diff --git a/app/browser/webtorrent.js b/app/browser/webtorrent.js new file mode 100644 index 00000000000..2057db4090e --- /dev/null +++ b/app/browser/webtorrent.js @@ -0,0 +1,102 @@ +const electron = require('electron') +const ipc = electron.ipcMain +const messages = require('../../js/constants/messages') +const WebTorrent = require('webtorrent') + +var DEBUG_IPC = false // Set to see communication between WebTorrent and torrent viewer tabs +var ANNOUNCE = [ + 'wss://tracker.btorrent.xyz', + 'wss://tracker.openwebtorrent.com', + 'wss://tracker.fastcast.nz' +] + +var client = null +var channels = {} + +ipc.on(messages.TORRENT_MESSAGE, function (e, msg) { + if (DEBUG_IPC) console.log('Received IPC: ' + JSON.stringify(msg)) + channels[msg.channelID] = e.sender + handleMessage(msg) +}) + +function handleMessage (msg) { + switch (msg.type) { + case 'add': + return handleAdd(msg) + default: + // Sanity check. Is there a better way to do error logging in the browser process? + console.error('Ignoring unknown action ' + msg.type + ', channel ' + msg.channelID) + } +} + +function handleAdd (msg) { + var torrent = lazyClient().add(msg.torrentID, { + announce: ANNOUNCE + }) + if (torrent.channelID) throw new Error('torrent already has a channelID') + // TODO: handle the case where two different tabs (two different channels) + // both open the same infohash + torrent.channelID = msg.channelID + addTorrentEvents(torrent) +} + +function addClientEvents () { + client.on('error', function (err) { + sendToAllChannels({errorMessage: err.message}) + }) +} + +function addTorrentEvents (torrent) { + torrent.on('infohash', () => sendInfo(torrent)) + torrent.on('metadata', () => sendInfo(torrent)) + torrent.on('progress', () => sendProgress(torrent)) + torrent.on('done', () => sendProgress(torrent)) +} + +function sendProgress (torrent) { + send({ + type: 'progress', + channelID: torrent.channelID, + progress: torrent.progress + }) +} + +function sendInfo (torrent) { + var msg = { + type: 'info', + channelID: torrent.channelID, + torrent: { + name: torrent.name, + infohash: torrent.infohash, + progress: torrent.progress, + files: [] + } + } + if (torrent.files) { + msg.torrent.files = torrent.files.map(function (file) { + return { + name: file.name + } + }) + } + send(msg) +} + +function send (msg) { + if (DEBUG_IPC) console.log('Sending IPC: ' + JSON.stringify(msg)) + channels[msg.channelID].send(messages.TORRENT_MESSAGE, msg) +} + +function sendToAllChannels (msg) { + for (var channelID in channels) { + var channelMsg = Object.assign({}, msg, {channelID}) + send(channelMsg) + } +} + +function lazyClient () { + if (client) return client + client = new WebTorrent() + addClientEvents() + return client +} diff --git a/app/index.js b/app/index.js index 73b75fe5b7f..76c4fae87b8 100644 --- a/app/index.js +++ b/app/index.js @@ -82,6 +82,7 @@ const basicAuth = require('./browser/basicAuth') const async = require('async') const tabs = require('./browser/tabs') const settings = require('../js/constants/settings') +require('./browser/webtorrent') // temporary fix for #4517, #4518 and #4472 app.commandLine.appendSwitch('enable-use-zoom-for-dsf', 'false') diff --git a/js/components/frame.js b/js/components/frame.js index 55eac87a895..13d6163f0d3 100644 --- a/js/components/frame.js +++ b/js/components/frame.js @@ -884,10 +884,31 @@ class Frame extends ImmutableComponent { method = (currentDetail, originalDetail) => windowActions.setAutofillCreditCardDetail(currentDetail, originalDetail) break + case messages.TORRENT_MESSAGE: + // Relay torrent IPC from the webview to a browser process + method = (message) => { + if (typeof message.channelID !== 'string') { + throw new Error('Invalid or missing channelID: ' + JSON.stringify(message)) + } + if (this.torrentChannelID && this.torrentChannelID !== message.channelID) { + throw new Error('ChannelID changed, expected ' + this.torrentChannelID + + ': ' + JSON.stringify(message)) + } + this.torrentChannelID = message.channelID + ipc.send(messages.TORRENT_MESSAGE, message) + } + break } method.apply(this, e.args) }) + // Relay torrent IPC from the browser process back to the webview + ipc.on(messages.TORRENT_MESSAGE, (e, obj) => { + // Ignore the message if it's for a different tab + if (obj.channelID !== this.torrentChannelID) return + this.webview.send(messages.TORRENT_MESSAGE, obj) + }) + const interceptFlash = (stopCurrentLoad, adobeUrl, redirectUrl) => { if (!this.origin) { return diff --git a/js/constants/messages.js b/js/constants/messages.js index ee9a6d70d75..bca63fc32d0 100644 --- a/js/constants/messages.js +++ b/js/constants/messages.js @@ -150,7 +150,9 @@ const messages = { LEDGER_UPDATED: _, LEDGER_CREATE_WALLET: _, CHECK_BITCOIN_HANDLER: _, - ADD_FUNDS_CLOSED: _ + ADD_FUNDS_CLOSED: _, + // Torrent + TORRENT_MESSAGE: _ } module.exports = mapValuesByKeys(messages) diff --git a/js/webtorrent/entry.js b/js/webtorrent/entry.js index af9c7ced37e..41abf4926a8 100644 --- a/js/webtorrent/entry.js +++ b/js/webtorrent/entry.js @@ -1,53 +1,63 @@ const ReactDOM = require('react-dom') const magnetURI = require('magnet-uri') const Button = require('../components/button') +const messages = require('../constants/messages') +const uuid = require('uuid') -/** - * TODO: Once WebTorrent is running in it's own process, replace the window - * globals with require() calls. Delete ext/webtorrent.min.js. - */ -const WebTorrent = window.WebTorrent // require('webtorrent') - -/** - * TODO: Replace hard-coded list of community trackers with latest list from - * create-torrent package, similar to how WebTorrent Desktop does it here: - * https://github.com/feross/webtorrent-desktop/blob/4bb2056bc9c1a421815b97d03ffed512575dfde0/src/renderer/webtorrent.js#L29-L31 - */ -window.WEBTORRENT_ANNOUNCE = [ - 'wss://tracker.btorrent.xyz', - 'wss://tracker.openwebtorrent.com', - 'wss://tracker.fastcast.nz' -] - -var state = { - torrentId: window.location.hash.substring(1), +require('../../less/webtorrent.less') + +// Set window.state for easier debugging +var state = window.state = { + torrentID: window.location.hash.substring(1), torrent: null, - progress: 0, - files: [], dn: '', errorMessage: '', isFileLoaded: false } -require('../../less/webtorrent.less') - -// Start downloading the torrent -var client = window.client = new WebTorrent() -client.on('error', onError) - // Show download progress setInterval(render, 1000) render() +// Talk to the webtorrent process +var CHANNEL_ID = uuid.v4() +var ipc = window.chrome.ipc +ipc.on(messages.TORRENT_MESSAGE, function (e, msg) { + switch (msg.type) { + case 'info': + return handleInfo(msg) + case 'progress': + return handleProgress(msg) + case 'error': + return handleError(msg) + } +}) + +function handleInfo (msg) { + state.torrent = msg.torrent +} + +function handleProgress (msg) { + if (state.torrent) state.torrent.progress = msg.progress +} + +function handleError (msg) { + state.errorMessage = msg.errorMessage +} + function start () { - state.torrent = client.add(state.torrentId) + ipc.sendToHost(messages.TORRENT_MESSAGE, { + channelID: CHANNEL_ID, + type: 'add', + torrentID: state.torrentID + }) } function render () { var torrent = state.torrent - var id = state.torrentId + var id = state.torrentID - // TODO: right now, we only support magnet links as the torrentId + // TODO: right now, we only support magnet links as the torrentID // Eventually, we need to support .torrent files as well, prob using parse-torrent if (!state.magnetInfo && id.startsWith('magnet:?')) { state.magnetInfo = magnetURI(id) @@ -94,18 +104,14 @@ function render () { ) } else if (state.magnetInfo && state.magnetInfo.ix != null) { + // TODO: load over HTTP, either into an