Skip to content

Commit

Permalink
refactor(browserAction): rewrite update logic
Browse files Browse the repository at this point in the history
This replaces alarm-based updates with message-based signalling
based on `browser.runtime.connect` API

Chrome does not support Alarm intervals smaller than one minute,
so we will replace all of them with this new type of signalling.
This is yet another piece of #218 (and #259) efforts.

This commit also closes #243 by hiding broken actions in 'incognito' mode.
  • Loading branch information
lidel committed Jul 8, 2017
1 parent 7620870 commit a10ce15
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 68 deletions.
54 changes: 52 additions & 2 deletions add-on/src/lib/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,20 +221,70 @@ function readDnslinkTxtRecordFromApi (fqdn) {
})
}

// PORTS
// ===================================================================
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/connect
// Make a connection between different contexts inside the add-on,
// e.g. signalling between browser action popup and background page that works
// in everywhere, even in private contexts (https://github.com/ipfs/ipfs-companion/issues/243)

const browserActionPortName = 'browser-action-port'
var browserActionPort

browser.runtime.onConnect.addListener(port => {
// console.log('onConnect', port)
if (port.name === browserActionPortName) {
browserActionPort = port
browserActionPort.onMessage.addListener(handleMessageFromBrowserAction)
browserActionPort.onDisconnect.addListener(() => { browserActionPort = null })
sendStatusUpdateToBrowserAction()
}
})

function handleMessageFromBrowserAction (message) {
// console.log('In background script, received message from browser action', message)
if (message.event === 'notification') {
notify(message.title, message.message)
}
}

async function sendStatusUpdateToBrowserAction () {
if (browserActionPort) {
const info = {
peerCount: state.peerCount,
currentTab: await browser.tabs.query({active: true, currentWindow: true}).then(tabs => tabs[0])
}
try {
let v = await ipfs.version()
if (v) {
info.gatewayVersion = v.commit ? v.version + '/' + v.commit : v.version
}
} catch (error) {
info.gatewayVersion = null
}
if (info.currentTab) {
info.ipfsPageActionsContext = isIpfsPageActionsContext(info.currentTab.url)
}
browserActionPort.postMessage({statusUpdate: info})
}
}

// ALARMS
// ===================================================================
// TODO: Remove use of alarms for signal passing, use PORTS instead

const ipfsApiStatusUpdateAlarm = 'ipfs-api-status-update'
const ipfsRedirectUpdateAlarm = 'ipfs-redirect-update'

function handleAlarm (alarm) {
async function handleAlarm (alarm) {
// avoid making expensive updates when IDLE
if (alarm.name === ipfsApiStatusUpdateAlarm) {
getSwarmPeerCount()
await getSwarmPeerCount()
.then(updatePeerCountState)
.then(updateAutomaticModeRedirectState)
.then(updateBrowserActionBadge)
.then(updateContextMenus)
sendStatusUpdateToBrowserAction()
}
}

Expand Down
133 changes: 67 additions & 66 deletions add-on/src/popup/browser-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const ipfsIconOn = '../../icons/ipfs-logo-on.svg'
const ipfsIconOff = '../../icons/ipfs-logo-off.svg'
const offline = 'offline'

var port
var state

function resolv (element) {
// lookup DOM if element is just a string ID
if (Object.prototype.toString.call(element) === '[object String]') {
Expand Down Expand Up @@ -49,28 +52,24 @@ function getBackgroundPage () {
return browser.runtime.getBackgroundPage()
}

function getCurrentTab () {
return browser.tabs.query({active: true, currentWindow: true}).then(tabs => tabs[0])
function notify (title, message) {
port.postMessage({event: 'notification', title: title, message: message})
}

// Ipfs Context Page Actions
// ===================================================================

async function copyCurrentPublicGwAddress () {
const bg = await getBackgroundPage()
const currentTab = await getCurrentTab()
const publicGwAddress = new URL(currentTab.url.replace(bg.state.gwURLString, 'https://ipfs.io/')).toString()
const publicGwAddress = new URL(state.currentTab.url.replace(state.gwURLString, 'https://ipfs.io/')).toString()
copyTextToClipboard(publicGwAddress)
bg.notify('notify_copiedPublicURLTitle', publicGwAddress)
notify('notify_copiedPublicURLTitle', publicGwAddress)
window.close()
}

async function copyCurrentCanonicalAddress () {
const bg = await getBackgroundPage()
const currentTab = await getCurrentTab()
const rawIpfsAddress = currentTab.url.replace(/^.+(\/ip(f|n)s\/.+)/, '$1')
const rawIpfsAddress = state.currentTab.url.replace(/^.+(\/ip(f|n)s\/.+)/, '$1')
copyTextToClipboard(rawIpfsAddress)
bg.notify('notify_copiedCanonicalAddressTitle', rawIpfsAddress)
notify('notify_copiedCanonicalAddressTitle', rawIpfsAddress)
window.close()
}

Expand All @@ -89,11 +88,10 @@ async function pinCurrentResource () {
deactivatePinButton()
try {
const bg = await getBackgroundPage()
const currentTab = await getCurrentTab()
const currentPath = await resolveToIPFS(new URL(currentTab.url).pathname)
const currentPath = await resolveToIPFS(new URL(state.currentTab.url).pathname)
const pinResult = await bg.ipfs.pin.add(currentPath, { recursive: true })
console.log('ipfs.pin.add result', pinResult)
bg.notify('notify_pinnedIpfsResourceTitle', currentPath)
notify('notify_pinnedIpfsResourceTitle', currentPath)
} catch (error) {
handlePinError('notify_pinErrorTitle', error)
}
Expand All @@ -104,11 +102,10 @@ async function unpinCurrentResource () {
deactivatePinButton()
try {
const bg = await getBackgroundPage()
const currentTab = await getCurrentTab()
const currentPath = await resolveToIPFS(new URL(currentTab.url).pathname)
const currentPath = await resolveToIPFS(new URL(state.currentTab.url).pathname)
const result = await bg.ipfs.pin.rm(currentPath, {recursive: true})
console.log('ipfs.pin.rm result', result)
bg.notify('notify_unpinnedIpfsResourceTitle', currentPath)
notify('notify_unpinnedIpfsResourceTitle', currentPath)
} catch (error) {
handlePinError('notify_unpinErrorTitle', error)
}
Expand Down Expand Up @@ -140,8 +137,7 @@ async function handlePinError (errorMessageKey, error) {
console.error(browser.i18n.getMessage(errorMessageKey), error)
deactivatePinButton()
try {
const bg = await getBackgroundPage()
bg.notify(errorMessageKey, error.message)
notify(errorMessageKey, error.message)
} catch (error) {
console.error('unable to access background page', error)
}
Expand All @@ -159,8 +155,7 @@ async function resolveToIPFS (path) {
async function activatePinButton () {
try {
const bg = await getBackgroundPage()
const currentTab = await getCurrentTab()
const currentPath = await resolveToIPFS(new URL(currentTab.url).pathname)
const currentPath = await resolveToIPFS(new URL(state.currentTab.url).pathname)
const response = await bg.ipfs.pin.ls(currentPath, {quiet: true})
console.log(`positive ipfs.pin.ls for ${currentPath}: ${JSON.stringify(response)}`)
activateUnpinning()
Expand All @@ -176,21 +171,27 @@ async function activatePinButton () {
}

async function updatePageActions () {
// console.log('running updatePageActions()')
try {
const bg = await getBackgroundPage()
const currentTab = await getCurrentTab()
if (bg.isIpfsPageActionsContext(currentTab.url)) {
deactivatePinButton()
show(ipfsContextActions)
copyPublicGwAddressButton.onclick = copyCurrentPublicGwAddress
copyIpfsAddressButton.onclick = copyCurrentCanonicalAddress
// IPFS contexts require access to background page
// which is denied in Private Browsing mode
const bg = await getBackgroundPage()

// Check if current page is an IPFS one
const ipfsContext = bg && state && state.ipfsPageActionsContext

// There is no point in displaying actions that require API interaction if API is down
const apiIsUp = state && state.peerCount >= 0

if (ipfsContext) {
show(ipfsContextActions)
copyPublicGwAddressButton.onclick = copyCurrentPublicGwAddress
copyIpfsAddressButton.onclick = copyCurrentCanonicalAddress
if (apiIsUp) {
activatePinButton()
} else {
hide(ipfsContextActions)
deactivatePinButton()
}
} catch (error) {
console.error(`Error while setting up pageAction: ${error}`)
} else {
hide(ipfsContextActions)
}
}

Expand Down Expand Up @@ -223,6 +224,7 @@ openPreferences.onclick = () => {
}

async function updateBrowserActionPopup () {
updatePageActions()
// update redirect status
const options = await browser.storage.local.get()
try {
Expand All @@ -247,46 +249,45 @@ async function updateBrowserActionPopup () {
set('gateway-address-val', '???')
}

try {
const background = await browser.runtime.getBackgroundPage()
if (background.ipfs) {
// update swarm peer count
try {
const peerCount = background.state.peerCount
set('swarm-peers-val', peerCount < 0 ? offline : peerCount)
ipfsIcon.src = peerCount > 0 ? ipfsIconOn : ipfsIconOff
if (peerCount > 0) { // API is online & there are peers
show('quick-upload')
} else {
hide('quick-upload')
}
if (peerCount < 0) { // API is offline
hide('open-webui')
} else {
show('open-webui')
}
} catch (error) {
console.error(`Unable update peer count due to ${error}`)
}
// update gateway version
try {
const v = await background.ipfs.version()
set('gateway-version-val', (v.commit ? v.version + '/' + v.commit : v.version))
} catch (error) {
set('gateway-version-val', offline)
}
if (state) {
// update swarm peer count
const peerCount = state.peerCount
set('swarm-peers-val', peerCount < 0 ? offline : peerCount)
ipfsIcon.src = peerCount > 0 ? ipfsIconOn : ipfsIconOff
if (peerCount > 0) { // API is online & there are peers
show('quick-upload')
} else {
hide('quick-upload')
}
} catch (error) {
console.error(`Error while accessing background page: ${error}`)
if (peerCount < 0) { // API is offline
hide('open-webui')
} else {
show('open-webui')
}
// update gateway version
set('gateway-version-val', state.gatewayVersion ? state.gatewayVersion : offline)
}
}

// hide things that cause ugly reflow if removed later
deactivatePinButton()
hide(ipfsContextActions)
hide('quick-upload')
hide('open-webui')

// listen to any changes and update diagnostics
browser.alarms.onAlarm.addListener(updateBrowserActionPopup)
document.addEventListener('DOMContentLoaded', updatePageActions)
document.addEventListener('DOMContentLoaded', updateBrowserActionPopup)
function onDOMContentLoaded () {
// set up initial layout that will remain if there is no peers
updateBrowserActionPopup()
// initialize connection to the background script which will trigger UI updates
port = browser.runtime.connect({name: 'browser-action-port'})
port.onMessage.addListener((message) => {
if (message.statusUpdate) {
// console.log('In browser action, received message from background:', message)
state = message.statusUpdate
updateBrowserActionPopup()
}
})
}

// init
document.addEventListener('DOMContentLoaded', onDOMContentLoaded)

0 comments on commit a10ce15

Please sign in to comment.