Skip to content

Commit

Permalink
Merge branch 'feat/upgrade-deps' of github.com:tableflip/ipfs-compani…
Browse files Browse the repository at this point in the history
…on into feat/upgrade-deps
  • Loading branch information
alanshaw committed Feb 21, 2018
2 parents e6c7ac3 + 2b13fdd commit 9dc2bc4
Show file tree
Hide file tree
Showing 14 changed files with 465 additions and 42 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,5 @@ Each `npm` task can be run separately. The most useful ones are:
```bash
export PATH="/path/to/alternative/version/of/firefox/:${PATH}"
```

- [Testing persistent and restart features](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Testing_persistent_and_restart_features)
2 changes: 1 addition & 1 deletion add-on/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"applications": {
"gecko": {
"id": "[email protected]",
"strict_min_version": "57.0"
"strict_min_version": "58.0"
}
},

Expand Down
2 changes: 1 addition & 1 deletion add-on/src/lib/copier.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ async function copyTextToClipboard (copyText) {

// In Firefox you can't select text or focus an input field in background pages,
// so you can't write to the clipboard from a background page.
// We work around this limitation by injecting content scropt into a tab and copying there.
// We work around this limitation by injecting content script into a tab and copying there.
// Yes, this is 2017.
try {
const copyHelperPresent = (await browser.tabs.executeScript(tabId, { runAt: 'document_start', code: "typeof copyToClipboardIn2017 === 'function';" }))[0]
Expand Down
13 changes: 9 additions & 4 deletions add-on/src/lib/dns-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,15 @@ module.exports = function createDnsLink (getState) {
}
},

redirectToIpnsPath (url) {
const fqdn = url.hostname
url.protocol = getState().gwURL.protocol
url.host = getState().gwURL.host
redirectToIpnsPath (originalUrl) {
// TODO: redirect to `ipns://` if hasNativeProtocolHandler === true
const fqdn = originalUrl.hostname
const state = getState()
const gwUrl = state.ipfsNodeType === 'embedded' ? state.pubGwURL : state.gwURL
const url = new URL(originalUrl)
url.protocol = gwUrl.protocol
url.host = gwUrl.host
url.port = gwUrl.port
url.pathname = `/ipns/${fqdn}${url.pathname}`
return { redirectUrl: url.toString() }
}
Expand Down
47 changes: 24 additions & 23 deletions add-on/src/lib/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

const browser = require('webextension-polyfill')
const { optionDefaults, storeMissingOptions } = require('./options')
const { initState } = require('./state')
const { initState, offlinePeerCount } = require('./state')
const { createIpfsPathValidator, urlAtPublicGw } = require('./ipfs-path')
const createDnsLink = require('./dns-link')
const { createRequestModifier } = require('./ipfs-request')
const { initIpfsClient, destroyIpfsClient } = require('./ipfs-client')
const { createIpfsUrlProtocolHandler } = require('./ipfs-protocol')
const createNotifier = require('./notifier')
const createCopier = require('./copier')
const createRuntimeChecks = require('./runtime-checks')
const { createContextMenus, findUrlForContext } = require('./context-menus')
const createIpfsProxy = require('./ipfs-proxy')

Expand All @@ -25,15 +26,16 @@ module.exports = async function init () {
var modifyRequest
var notify
var copier
var runtime
var contextMenus
var apiStatusUpdateInterval
var ipfsProxy
const offlinePeerCount = -1
const idleInSecs = 5 * 60
const browserActionPortName = 'browser-action-port'

try {
const options = await browser.storage.local.get(optionDefaults)
runtime = await createRuntimeChecks(browser)
state = initState(options)
ipfs = await initIpfsClient(state)
notify = createNotifier(getState)
Expand Down Expand Up @@ -73,12 +75,12 @@ module.exports = async function init () {
browser.tabs.onActivated.addListener(onActivatedTab)
browser.runtime.onMessage.addListener(onRuntimeMessage)
browser.runtime.onConnect.addListener(onRuntimeConnect)
// browser.protocol exists only in Brave
if (browser.protocol && browser.protocol.registerStringProtocol) {

if (runtime.hasNativeProtocolHandler) {
console.log('[ipfs-companion] registerStringProtocol available. Adding ipfs:// handler')
browser.protocol.registerStringProtocol('ipfs', createIpfsUrlProtocolHandler(() => ipfs))
} else {
console.log('[ipfs-companion] registerStringProtocol not available, native protocol will not be registered', browser.protocol)
console.log('[ipfs-companion] browser.protocol.registerStringProtocol not available, native protocol will not be registered')
}
}

Expand Down Expand Up @@ -185,10 +187,6 @@ module.exports = async function init () {
// GUI
// ===================================================================

function inFirefox () {
return !!navigator.userAgent.match('Firefox')
}

function preloadAtPublicGateway (path) {
// asynchronous HTTP HEAD request preloads triggers content without downloading it
return new Promise((resolve, reject) => {
Expand All @@ -215,7 +213,7 @@ module.exports = async function init () {
const srcUrl = await findUrlForContext(info)
let result
try {
if (inFirefox()) {
if (runtime.isFirefox) {
// workaround due to https://github.com/ipfs/ipfs-companion/issues/227
const fetchOptions = {
cache: 'force-cache',
Expand Down Expand Up @@ -255,23 +253,23 @@ module.exports = async function init () {
}

// TODO: feature detect and push to client type specific modules.
function getIpfsPathAndLocalAddress (hash) {
function getIpfsPathAndNativeAddress (hash) {
const path = `/ipfs/${hash}`
if (state.ipfsNodeType === 'embedded' && browser && browser.protocol && browser.protocol.registerStringProtocol) {
return {path, localAddress: `ipfs://${hash}`}
if (runtime.hasNativeProtocolHandler) {
return {path, url: `ipfs://${hash}`}
} else {
// Use the chosen gateway... local or public
const url = new URL(path, state.gwURLString).toString()
return {path, localAddress: url}
// open at public GW (will be redirected to local elsewhere, if enabled)
const url = new URL(path, state.pubGwURLString).toString()
return {path, url: url}
}
}

function uploadResultHandler (result) {
result.forEach(function (file) {
if (file && file.hash) {
const {path, localAddress} = getIpfsPathAndLocalAddress(file.hash)
const {path, url} = getIpfsPathAndNativeAddress(file.hash)
browser.tabs.create({
'url': localAddress
'url': url
})
console.info('[ipfs-companion] successfully stored', path)
if (state.preloadAtPublicGateway) {
Expand Down Expand Up @@ -354,11 +352,14 @@ module.exports = async function init () {
}

async function apiStatusUpdate () {
// update peer count
let oldPeerCount = state.peerCount
state.peerCount = await getSwarmPeerCount()
state.repoStats = await getRepoStats()
updatePeerCountDependentStates(oldPeerCount, state.peerCount)
sendStatusUpdateToBrowserAction()
// update repo stats
state.repoStats = await getRepoStats()
// trigger pending updates
await sendStatusUpdateToBrowserAction()
}

function updatePeerCountDependentStates (oldPeerCount, newPeerCount) {
Expand Down Expand Up @@ -486,11 +487,11 @@ module.exports = async function init () {
// enable/disable gw redirect based on API going online or offline
// newPeerCount === -1 currently implies node is offline.
// TODO: use `node.isOnline()` if available (js-ipfs)
if (state.automaticMode) {
if (oldPeerCount === -1 && newPeerCount > -1 && !state.redirect) {
if (state.automaticMode && state.ipfsNodeType !== 'embedded') {
if (oldPeerCount === offlinePeerCount && newPeerCount > offlinePeerCount && !state.redirect) {
browser.storage.local.set({useCustomGateway: true})
.then(() => notify('notify_apiOnlineTitle', 'notify_apiOnlineAutomaticModeMsg'))
} else if (oldPeerCount > -1 && newPeerCount === -1 && state.redirect) {
} else if (newPeerCount === offlinePeerCount && state.redirect) {
browser.storage.local.set({useCustomGateway: false})
.then(() => notify('notify_apiOfflineTitle', 'notify_apiOfflineAutomaticModeMsg'))
}
Expand Down
12 changes: 10 additions & 2 deletions add-on/src/lib/ipfs-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ function createRequestModifier (getState, dnsLink, ipfsPathValidator) {
}
}

// skip requests to the public gateway if embedded node is running (otherwise we have too much recursion)
if (state.ipfsNodeType === 'embedded' && request.url.startsWith(state.pubGwURLString)) {
return
// TODO: do not skip and redirect to `ipfs://` and `ipns://` if hasNativeProtocolHandler === true
}

// handle redirects to custom gateway
if (state.redirect) {
// Ignore preload requests
Expand All @@ -43,7 +49,7 @@ function createRequestModifier (getState, dnsLink, ipfsPathValidator) {
}
// Detect valid /ipfs/ and /ipns/ on any site
if (ipfsPathValidator.publicIpfsOrIpnsResource(request.url)) {
return redirectToCustomGateway(request.url, state.gwURL)
return redirectToGateway(request.url, state)
}
// Look for dnslink in TXT records of visited sites
if (state.dnslink && dnsLink.isDnslookupSafeForURL(request.url)) {
Expand All @@ -55,7 +61,9 @@ function createRequestModifier (getState, dnsLink, ipfsPathValidator) {

exports.createRequestModifier = createRequestModifier

function redirectToCustomGateway (requestUrl, gwUrl) {
function redirectToGateway (requestUrl, state) {
// TODO: redirect to `ipfs://` if hasNativeProtocolHandler === true
const gwUrl = state.ipfsNodeType === 'embedded' ? state.pubGwURL : state.gwURL
const url = new URL(requestUrl)
url.protocol = gwUrl.protocol
url.host = gwUrl.host
Expand Down
2 changes: 1 addition & 1 deletion add-on/src/lib/notifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function createNotifier (getState) {
} else {
message = messageKey
}
if (getState().displayNotifications) {
if (getState().displayNotifications && browser && browser.notifications.create) {
browser.notifications.create({
type: 'basic',
iconUrl: browser.extension.getURL('icons/ipfs-logo-on.svg'),
Expand Down
35 changes: 35 additions & 0 deletions add-on/src/lib/runtime-checks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict'
/* eslint-env browser, webextensions */

function getBrowserInfo (browser) {
if (browser && browser.runtime && browser.runtime.getBrowserInfo) {
return browser.runtime.getBrowserInfo()
}
return Promise.resolve()
}

function getPlatformInfo (browser) {
if (browser && browser.runtime && browser.runtime.getPlatformInfo) {
return browser.runtime.getPlatformInfo()
}
return Promise.resolve()
}

async function createRuntimeChecks (browser) {
// browser
const browserInfo = await getBrowserInfo(browser)
const runtimeBrowserName = browserInfo ? browserInfo.name : 'unknown'
const runtimeIsFirefox = !!runtimeBrowserName.match('Firefox')
const runtimeHasNativeProtocol = !!(browser && browser.protocol && browser.protocol.registerStringProtocol)
// platform
const platformInfo = await getPlatformInfo(browser)
const runtimeIsAndroid = platformInfo ? platformInfo.os === 'android' : false
//
return Object.freeze({
isFirefox: runtimeIsFirefox,
isAndroid: runtimeIsAndroid,
hasNativeProtocolHandler: runtimeHasNativeProtocol
})
}

module.exports = createRuntimeChecks
6 changes: 5 additions & 1 deletion add-on/src/lib/state.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
'use strict'
/* eslint-env browser */
/* eslint-env browser, webextensions */

const offlinePeerCount = -1

function initState (options) {
const state = {}
// we store the most used values in optimized form
// to minimize performance impact on overall browsing experience
state.peerCount = offlinePeerCount
state.ipfsNodeType = options.ipfsNodeType
state.pubGwURL = new URL(options.publicGatewayUrl)
state.pubGwURLString = state.pubGwURL.toString()
Expand All @@ -24,3 +27,4 @@ function initState (options) {
}

exports.initState = initState
exports.offlinePeerCount = offlinePeerCount
8 changes: 5 additions & 3 deletions add-on/src/popup/browser-action/context-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const html = require('choo/html')
const navItem = require('./nav-item')

module.exports = function contextActions ({
ipfsNodeType,
isIpfsContext,
isPinning,
isUnPinning,
Expand All @@ -17,6 +18,7 @@ module.exports = function contextActions ({
onUnPin
}) {
if (!isIpfsContext) return null
const isPinningSupported = (ipfsNodeType !== 'embedded')

return html`
<div class="bb b--black-20 mv2 pb2">
Expand All @@ -28,14 +30,14 @@ module.exports = function contextActions ({
text: browser.i18n.getMessage('panel_copyCurrentPublicGwUrl'),
onClick: onCopyPublicGwAddr
})}
${isIpfsOnline && isPinned ? null : (
${isIpfsOnline && isPinningSupported && !isPinned ? (
navItem({
text: browser.i18n.getMessage('panel_pinCurrentIpfsAddress'),
disabled: isPinning,
onClick: onPin
})
)}
${isIpfsOnline && isPinned ? (
) : null}
${isIpfsOnline && isPinningSupported && isPinned ? (
navItem({
text: browser.i18n.getMessage('panel_unpinCurrentIpfsAddress'),
disabled: isUnPinning,
Expand Down
34 changes: 30 additions & 4 deletions test/functional/lib/dns-link.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
'use strict'
const { describe, it } = require('mocha')
const { describe, it, before, after } = require('mocha')
const { expect } = require('chai')
const { URL } = require('url')
const createDnsLink = require('../../../add-on/src/lib/dns-link')

// https://github.com/ipfs/ipfs-companion/issues/303
describe('DNSLINK', function () {
describe('redirectToIpnsPath(url)', function () {
it('should return IPNS path at a custom gateway', function () {
before(() => {
global.URL = URL
})

describe('redirectToIpnsPath(url) with external gateway', function () {
it('should return IPNS path at a custom gateway', async function () {
const url = new URL('http://ipfs.git.sexy/sketches/ipld_intro.html?a=b#c=d')
const getState = () => ({ gwURL: new URL('http://127.0.0.1:8080') })
const getState = () => ({
gwURL: new URL('http://127.0.0.1:8080'),
pubGwURL: new URL('https://ipfs.io'),
ipfsNodeType: 'external'
})
const dnsLink = createDnsLink(getState)
expect(dnsLink.redirectToIpnsPath(url).redirectUrl)
.to.equal('http://127.0.0.1:8080/ipns/ipfs.git.sexy/sketches/ipld_intro.html?a=b#c=d')
})
})

describe('redirectToIpnsPath(url) with embedded gateway', function () {
it('should return IPNS path at a public gateway', async function () {
const url = new URL('http://ipfs.git.sexy/sketches/ipld_intro.html?a=b#c=d')
const getState = () => ({
gwURL: new URL('http://127.0.0.1:8080'),
pubGwURL: new URL('https://ipfs.io'),
ipfsNodeType: 'embedded'
})
const dnsLink = createDnsLink(getState)
expect(dnsLink.redirectToIpnsPath(url).redirectUrl)
.to.equal('https://ipfs.io/ipns/ipfs.git.sexy/sketches/ipld_intro.html?a=b#c=d')
})
})

after(() => {
delete global.URL
})
})
Loading

0 comments on commit 9dc2bc4

Please sign in to comment.