From eb8723a194f1c3bff606969fbc56fa8c76144276 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Sun, 20 Jan 2019 17:19:52 +0100 Subject: [PATCH] feat: per-site redirect opt-out Changes: - moved global redirect toggle from Browser Action menu to the utility icon row, under "redirect" icon - added animation to utility icons - global redirect icon will enable integrations if clicked when in suspended state - menu items specific to the Active Tab are marked with additional border (just a prototype, needs refinement) - Redirect opt-out per site - new menu item in Active Tab section - when clicked on regular site toggles redirect for current FQDN and all its subdomains - when clicked on /ipns// (DNSLink) website, toggles redirect for - after redirect preference changes for current website, the tab is reloaded - DNSLink websites are reloaded to with URL change between IPNS path and original URL - redirect preference applies not only to requests to URLs with with FQDN of the active tab, but also to all subresource requests that have it in `originUrl` (Firefox) or `Referer` header (Chrome) --- add-on/_locales/en/messages.json | 24 +- add-on/src/lib/dnslink.js | 22 ++ add-on/src/lib/ipfs-companion.js | 12 +- add-on/src/lib/ipfs-path.js | 2 + add-on/src/lib/ipfs-request.js | 16 + add-on/src/lib/options.js | 3 +- .../popup/browser-action/browser-action.css | 11 + .../popup/browser-action/context-actions.js | 59 ++- .../popup/browser-action/gateway-status.js | 2 +- add-on/src/popup/browser-action/header.js | 8 +- add-on/src/popup/browser-action/icon.js | 2 +- add-on/src/popup/browser-action/nav-item.js | 4 +- add-on/src/popup/browser-action/operations.js | 14 +- add-on/src/popup/browser-action/page.js | 19 +- .../src/popup/browser-action/redirect-icon.js | 374 ++++++++++++++++++ add-on/src/popup/browser-action/store.js | 88 ++++- add-on/src/popup/page-action/page.js | 6 +- package.json | 2 +- yarn.lock | 22 +- 19 files changed, 606 insertions(+), 84 deletions(-) create mode 100644 add-on/src/popup/browser-action/redirect-icon.js diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 9b8ffaf98..96bd07812 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -11,6 +11,10 @@ "message": "Global toggle: Suspend all IPFS integrations", "description": "A label for an embedded IPFS node (panel_headerActiveToggleTitle)" }, + "panel_headerRedirectToggleTitle": { + "message": "Global toggle: Stop all gateway redirections", + "description": "A label for an embedded IPFS node (panel_headerActiveToggleTitle)" + }, "panel_statusOffline": { "message": "offline", "description": "A label in Node status section of Browser Action pop-up (panel_statusOffline)" @@ -47,13 +51,21 @@ "message": "Open Preferences of Browser Extension", "description": "A menu item in Browser Action pop-up (panel_openPreferences)" }, - "panel_switchToCustomGateway": { - "message": "Switch to Custom Gateway", - "description": "A menu item in Browser Action pop-up (panel_switchToCustomGateway)" + "panel_globalRedirectEnable": { + "message": "Enable All Redirects", + "description": "A menu item in Browser Action pop-up (panel_globalRedirectEnable)" + }, + "panel_activeTabSectionHeader": { + "message": "Active Tab", + "description": "A menu item in Browser Action pop-up (panel_activeTabSiteRedirectEnable)" + }, + "panel_activeTabSiteRedirectEnable": { + "message": "Restore Redirects on $1", + "description": "A menu item in Browser Action pop-up (panel_activeTabSiteRedirectEnable)" }, - "panel_switchToPublicGateway": { - "message": "Switch to Public Gateway", - "description": "A menu item in Browser Action pop-up (panel_switchToPublicGateway)" + "panel_activeTabSiteRedirectDisable": { + "message": "Disable Redirects on $1", + "description": "A menu item in Browser Action pop-up (panel_activeTabSiteRedirectDisable)" }, "panel_pinCurrentIpfsAddress": { "message": "Pin IPFS Resource", diff --git a/add-on/src/lib/dnslink.js b/add-on/src/lib/dnslink.js index 1c500852a..877e8c775 100644 --- a/add-on/src/lib/dnslink.js +++ b/add-on/src/lib/dnslink.js @@ -175,6 +175,28 @@ module.exports = function createDnslinkResolver (getState) { } const fqdn = url.hostname return `/ipns/${fqdn}${url.pathname}${url.search}${url.hash}` + }, + + // Test if URL contains a valid DNSLink hostname, FQDN in /ipns/ path + // and return original hostname if present + findDNSLinkHostname (url) { + const { hostname, pathname } = new URL(url) + // check //foo.tld/ipns/ + if (IsIpfs.ipnsPath(pathname)) { + // we may have false-positives here, so we do additional checks below + const ipnsRoot = pathname.match(/^\/ipns\/([^/]+)/)[1] + // console.log('findDNSLinkHostname ==> inspecting IPNS root', ipnsRoot) + // Ignore PeerIDs, match DNSLink only + if (!IsIpfs.cid(ipnsRoot) && dnslinkResolver.readAndCacheDnslink(ipnsRoot)) { + // console.log('findDNSLinkHostname ==> found DNSLink for FQDN in url.pathname: ', ipnsRoot) + return ipnsRoot + } + } + // check ///foo/bar + if (dnslinkResolver.readAndCacheDnslink(hostname)) { + // console.log('findDNSLinkHostname ==> found DNSLink for url.hostname', hostname) + return hostname + } } } diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index c3ddd6766..083e0a652 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -213,13 +213,17 @@ module.exports = async function init () { async function sendStatusUpdateToBrowserAction () { if (!browserActionPort) return + const dropSlash = url => url.replace(/\/$/, '') const info = { active: state.active, ipfsNodeType: state.ipfsNodeType, peerCount: state.peerCount, - gwURLString: state.gwURLString, - pubGwURLString: state.pubGwURLString, + gwURLString: dropSlash(state.gwURLString), + pubGwURLString: dropSlash(state.pubGwURLString), webuiRootUrl: state.webuiRootUrl, + apiURLString: dropSlash(state.apiURLString), + redirect: state.redirect, + noRedirectHostnames: state.noRedirectHostnames, currentTab: await browser.tabs.query({ active: true, currentWindow: true }).then(tabs => tabs[0]) } try { @@ -232,6 +236,9 @@ module.exports = async function init () { } if (info.currentTab) { info.ipfsPageActionsContext = ipfsPathValidator.isIpfsPageActionsContext(info.currentTab.url) + info.currentDnslinkFqdn = dnslinkResolver.findDNSLinkHostname(info.currentTab.url) + info.currentFqdn = info.currentDnslinkFqdn || new URL(info.currentTab.url).hostname + info.currentTabRedirectOptOut = info.noRedirectHostnames && info.noRedirectHostnames.includes(info.currentFqdn) } // Still here? if (browserActionPort) { @@ -641,6 +648,7 @@ module.exports = async function init () { case 'automaticMode': case 'detectIpfsPathHeader': case 'preloadAtPublicGateway': + case 'noRedirectHostnames': state[key] = change.newValue break } diff --git a/add-on/src/lib/ipfs-path.js b/add-on/src/lib/ipfs-path.js index 79200a545..f8acc0d32 100644 --- a/add-on/src/lib/ipfs-path.js +++ b/add-on/src/lib/ipfs-path.js @@ -64,6 +64,7 @@ function createIpfsPathValidator (getState, dnsLink) { }, // Test if actions such as 'copy URL', 'pin/unpin' should be enabled for the URL + // TODO: include hostname check for DNSLink and display option to copy CID even if no redirect isIpfsPageActionsContext (url) { return (IsIpfs.url(url) && !url.startsWith(getState().apiURLString)) || IsIpfs.subdomain(url) } @@ -110,6 +111,7 @@ function validIpnsPath (path, dnsLink) { // console.log('==> IPNS is a valid CID', ipnsRoot) return true } + // then see if there is an DNSLink entry for 'ipnsRoot' hostname if (dnsLink.readAndCacheDnslink(ipnsRoot)) { // console.log('==> IPNS for FQDN with valid dnslink: ', ipnsRoot) return true diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index e3fd40bbb..0626d1393 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -65,6 +65,22 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru if (request.url.startsWith('http://127.0.0.1') || request.url.startsWith('http://localhost') || request.url.startsWith('http://[::1]')) { ignore(request.requestId) } + // skip if a per-site redirect opt-out exists + const parentUrl = request.originUrl || request.initiator // FF: originUrl, Chrome: initiator + const fqdn = new URL(request.url).hostname + const parentFqdn = parentUrl && request.url !== parentUrl ? new URL(parentUrl).hostname : null + if (state.noRedirectHostnames.some(optout => + fqdn.endsWith(optout) || (parentFqdn && parentFqdn.endsWith(optout) + ))) { + ignore(request.requestId) + } + // additional checks limited to requests for root documents + if (request.type === 'main_frame') { + // trigger DNSLink lookup if status for root domain is not in cache yet + if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) { + (async () => dnslinkResolver.readAndCacheDnslink(parentFqdn || fqdn))() + } + } return isIgnored(request.requestId) } diff --git a/add-on/src/lib/options.js b/add-on/src/lib/options.js index a0023b683..0b5f85139 100644 --- a/add-on/src/lib/options.js +++ b/add-on/src/lib/options.js @@ -12,6 +12,7 @@ exports.optionDefaults = Object.freeze({ }, null, 2), publicGatewayUrl: 'https://ipfs.io', useCustomGateway: true, + noRedirectHostnames: [], automaticMode: true, linkify: false, dnslinkPolicy: 'best-effort', @@ -22,7 +23,7 @@ exports.optionDefaults = Object.freeze({ customGatewayUrl: 'http://127.0.0.1:8080', ipfsApiUrl: 'http://127.0.0.1:5001', ipfsApiPollMs: 3000, - ipfsProxy: true + ipfsProxy: true // window.ipfs }) // `storage` should be a browser.storage.local or similar diff --git a/add-on/src/popup/browser-action/browser-action.css b/add-on/src/popup/browser-action/browser-action.css index 608d8e0a4..4263af1c4 100644 --- a/add-on/src/popup/browser-action/browser-action.css +++ b/add-on/src/popup/browser-action/browser-action.css @@ -6,6 +6,17 @@ background-color: #F4F4F4; } +.header-icon:active { + color: #edf0f4; + transform: translateY(4px); +} +.header-icon[disabled], +.header-icon[disabled]:active { + cursor: not-allowed; + pointer-events: none; + transform: none; +} + .outline-0--focus:focus { outline: 0; } diff --git a/add-on/src/popup/browser-action/context-actions.js b/add-on/src/popup/browser-action/context-actions.js index df868c9d7..0ad84df76 100644 --- a/add-on/src/popup/browser-action/context-actions.js +++ b/add-on/src/popup/browser-action/context-actions.js @@ -6,8 +6,12 @@ const html = require('choo/html') const navItem = require('./nav-item') const { contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') -module.exports = function contextActions ({ +// Context Actions are displayed in Browser Action and Page Action (FF only) +function contextActions ({ active, + globalRedirectEnabled, + currentFqdn, + currentTabRedirectOptOut, ipfsNodeType, isIpfsContext, isPinning, @@ -15,14 +19,17 @@ module.exports = function contextActions ({ isPinned, isIpfsOnline, isApiAvailable, + onToggleSiteRedirect, onCopy, onPin, onUnPin }) { - if (!isIpfsContext) return null const activePinControls = active && isIpfsOnline && isApiAvailable && !(isPinning || isUnPinning) - return html` -
+ const activeSiteRedirectSwitch = active && globalRedirectEnabled && ipfsNodeType === 'external' + + const renderIpfsContextItems = () => { + if (!isIpfsContext) return + return html`
${navItem({ text: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), onClick: () => onCopy(contextMenuCopyAddressAtPublicGw) @@ -50,6 +57,50 @@ module.exports = function contextActions ({ onClick: onUnPin }) ) : null} +
+ ` + } + + const renderSiteRedirectToggle = () => { + if (!activeSiteRedirectSwitch) return + const siteRedirectToggleLabel = browser.i18n.getMessage( + globalRedirectEnabled && !currentTabRedirectOptOut + ? 'panel_activeTabSiteRedirectDisable' + : 'panel_activeTabSiteRedirectEnable', + currentFqdn + ) + return html` + ${navItem({ + text: siteRedirectToggleLabel, + title: siteRedirectToggleLabel, + addClass: 'truncate', + disabled: !activeSiteRedirectSwitch, + onClick: onToggleSiteRedirect + })} + ` + } + + return html` +
+ ${renderIpfsContextItems()} + ${renderSiteRedirectToggle()}
` } +module.exports.contextActions = contextActions + +// "Active Tab" section is displayed in Browser Action only +// if redirect can be toggled or current tab has any IPFS Context Actions +function activeTabActions (state) { + const showActiveTabSection = (state.active && state.globalRedirectEnabled && state.ipfsNodeType === 'external') || state.isIpfsContext + if (!showActiveTabSection) return + return html` +
+
+ ${browser.i18n.getMessage('panel_activeTabSectionHeader')} +
+ ${contextActions(state)} +
+ ` +} +module.exports.activeTabActions = activeTabActions diff --git a/add-on/src/popup/browser-action/gateway-status.js b/add-on/src/popup/browser-action/gateway-status.js index bc1eaa44c..bbe8dd411 100644 --- a/add-on/src/popup/browser-action/gateway-status.js +++ b/add-on/src/popup/browser-action/gateway-status.js @@ -26,7 +26,7 @@ module.exports = function gatewayStatus ({ swarmPeers, isIpfsOnline, ipfsNodeType, - redirectEnabled + globalRedirectEnabled }) { const api = ipfsApiUrl && ipfsNodeType === 'embedded' ? 'js-ipfs' : ipfsApiUrl return html` diff --git a/add-on/src/popup/browser-action/header.js b/add-on/src/popup/browser-action/header.js index 77ad6c7c8..69f5b56e1 100644 --- a/add-on/src/popup/browser-action/header.js +++ b/add-on/src/popup/browser-action/header.js @@ -5,11 +5,13 @@ const browser = require('webextension-polyfill') const html = require('choo/html') const logo = require('../logo') const powerIcon = require('./power-icon') +const redirectIcon = require('./redirect-icon') const optionsIcon = require('./options-icon') const gatewayStatus = require('./gateway-status') module.exports = function header (props) { - const { ipfsNodeType, active, onToggleActive, onOpenPrefs, isIpfsOnline } = props + const { ipfsNodeType, active, globalRedirectEnabled, onToggleActive, onToggleGlobalRedirect, onOpenPrefs, isIpfsOnline } = props + const showGlobalRedirectSwitch = ipfsNodeType === 'external' return html`
@@ -29,6 +31,10 @@ module.exports = function header (props) { title: 'panel_headerActiveToggleTitle', action: onToggleActive })} + ${showGlobalRedirectSwitch ? redirectIcon({ active: active && globalRedirectEnabled, + title: 'panel_headerRedirectToggleTitle', + action: onToggleGlobalRedirect + }) : null} ${optionsIcon({ active, title: 'panel_openPreferences', action: onOpenPrefs diff --git a/add-on/src/popup/browser-action/icon.js b/add-on/src/popup/browser-action/icon.js index 443a60609..b419fd4a1 100644 --- a/add-on/src/popup/browser-action/icon.js +++ b/add-on/src/popup/browser-action/icon.js @@ -6,7 +6,7 @@ const browser = require('webextension-polyfill') function icon ({ svg, title, active, action }) { return html` - ` diff --git a/add-on/src/popup/browser-action/operations.js b/add-on/src/popup/browser-action/operations.js index dea80e5c2..a671fe224 100644 --- a/add-on/src/popup/browser-action/operations.js +++ b/add-on/src/popup/browser-action/operations.js @@ -9,15 +9,12 @@ module.exports = function operations ({ active, ipfsNodeType, isIpfsOnline, - redirectEnabled, isApiAvailable, onQuickUpload, - onOpenWebUi, - onToggleRedirect + onOpenWebUi }) { const activeQuickUpload = active && isIpfsOnline && isApiAvailable const activeWebUI = active && isIpfsOnline && ipfsNodeType === 'external' - const activeGatewaySwitch = active && ipfsNodeType === 'external' return html`
@@ -27,15 +24,6 @@ module.exports = function operations ({ disabled: !activeQuickUpload, onClick: onQuickUpload })} - ${navItem({ - text: browser.i18n.getMessage( - redirectEnabled && activeGatewaySwitch - ? 'panel_switchToPublicGateway' - : 'panel_switchToCustomGateway' - ), - disabled: !activeGatewaySwitch, - onClick: onToggleRedirect - })} ${navItem({ text: browser.i18n.getMessage('panel_openWebui'), disabled: !activeWebUI, diff --git a/add-on/src/popup/browser-action/page.js b/add-on/src/popup/browser-action/page.js index 5910c6a1e..35135044b 100644 --- a/add-on/src/popup/browser-action/page.js +++ b/add-on/src/popup/browser-action/page.js @@ -3,7 +3,7 @@ const html = require('choo/html') const header = require('./header') -const contextActions = require('./context-actions') +const { activeTabActions } = require('./context-actions') const operations = require('./operations') // Render the browser action page: @@ -17,23 +17,20 @@ module.exports = function browserActionPage (state, emit) { const onQuickUpload = () => emit('quickUpload') const onOpenWebUi = () => emit('openWebUi') const onOpenPrefs = () => emit('openPrefs') - const onToggleRedirect = () => emit('toggleRedirect') + const onToggleGlobalRedirect = () => emit('toggleGlobalRedirect') + const onToggleSiteRedirect = () => emit('toggleSiteRedirect') const onToggleNodeType = () => emit('toggleNodeType') const onToggleActive = () => emit('toggleActive') - const headerProps = Object.assign({ onToggleNodeType, onToggleActive, onOpenPrefs }, state) - const contextActionsProps = Object.assign({ onCopy, onPin, onUnPin }, state) - const opsProps = Object.assign({ onQuickUpload, onOpenWebUi, onToggleRedirect }, state) + const headerProps = Object.assign({ onToggleNodeType, onToggleActive, onToggleGlobalRedirect, onOpenPrefs }, state) + const activeTabActionsProps = Object.assign({ onToggleSiteRedirect, onCopy, onPin, onUnPin }, state) + const opsProps = Object.assign({ onQuickUpload, onOpenWebUi }, state) return html`
${header(headerProps)} -
- ${contextActions(contextActionsProps)} -
-
- ${operations(opsProps)} -
+ ${operations(opsProps)} + ${activeTabActions(activeTabActionsProps)}
` } diff --git a/add-on/src/popup/browser-action/redirect-icon.js b/add-on/src/popup/browser-action/redirect-icon.js new file mode 100644 index 000000000..60cc896f9 --- /dev/null +++ b/add-on/src/popup/browser-action/redirect-icon.js @@ -0,0 +1,374 @@ +'use strict' +/* eslint-env browser, webextensions */ + +const html = require('choo/html') +const icon = require('./icon') + +function redirectIcon ({ + active, + title, + action, + size = '2rem' +}) { + // TODO: minimize below (its 100K right now..) + const svg = html` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ` + return icon({ + svg, + title, + active, + action + }) +} +module.exports = redirectIcon diff --git a/add-on/src/popup/browser-action/store.js b/add-on/src/popup/browser-action/store.js index 3271665f3..c0a1835e2 100644 --- a/add-on/src/popup/browser-action/store.js +++ b/add-on/src/popup/browser-action/store.js @@ -2,6 +2,7 @@ /* eslint-env browser, webextensions */ const browser = require('webextension-polyfill') +const IsIpfs = require('is-ipfs') const { safeIpfsPath, trimHashAndSearch } = require('../../lib/ipfs-path') const { contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') @@ -16,6 +17,8 @@ module.exports = (state, emitter) => { isUnPinning: false, isPinned: false, currentTab: null, + currentFqdn: null, + currentDnslinkFqdn: null, // IPFS status ipfsNodeType: 'external', isIpfsOnline: false, @@ -24,7 +27,9 @@ module.exports = (state, emitter) => { gatewayAddress: null, swarmPeers: null, gatewayVersion: null, - redirectEnabled: false, + noRedirectHostnames: [], + globalRedirectEnabled: false, + currentTabRedirectOptOut: false, isApiAvailable: false }) @@ -47,6 +52,7 @@ module.exports = (state, emitter) => { await updatePageActionsState(status) emitter.emit('render') } + console.log('statusAfterUpdate', status) } }) // fix for https://github.com/ipfs-shipyard/ipfs-companion/issues/318 @@ -142,22 +148,67 @@ module.exports = (state, emitter) => { }) }) - emitter.on('toggleRedirect', async () => { - const enabled = state.redirectEnabled - state.redirectEnabled = !enabled - state.gatewayAddress = '…' + emitter.on('toggleGlobalRedirect', async () => { + const redirectEnabled = state.globalRedirectEnabled + // If all integrations were suspended.. + if (!state.active) { + // ..clicking on 'inactive' toggle implies user wants to go online + emitter.emit('toggleActive') + // if redirect was already on, then we dont want to disable it, as it would be bad UX + if (redirectEnabled) return + } + state.globalRedirectEnabled = !redirectEnabled + state.gatewayAddress = state.globalRedirectEnabled ? state.gwURLString : state.pubGwURLString emitter.emit('render') try { - await browser.storage.local.set({ useCustomGateway: !enabled }) + await browser.storage.local.set({ useCustomGateway: !redirectEnabled }) } catch (error) { console.error(`Unable to update redirect state due to ${error}`) - state.redirectEnabled = enabled + state.globalRedirectEnabled = redirectEnabled } emitter.emit('render') }) + emitter.on('toggleSiteRedirect', async () => { + state.currentTabRedirectOptOut = !state.currentTabRedirectOptOut + emitter.emit('render') + + try { + let noRedirectHostnames = state.noRedirectHostnames + // if we are on /ipns/fqdn.tld/ then use hostname from DNSLink + let fqdn = state.currentDnslinkFqdn || state.currentFqdn + if (noRedirectHostnames.includes(fqdn)) { + noRedirectHostnames = noRedirectHostnames.filter(host => !host.endsWith(fqdn)) + } else { + noRedirectHostnames.push(fqdn) + } + console.dir('toggleSiteRedirect', state) + await browser.storage.local.set({ noRedirectHostnames }) + + // Reload the current tab to apply updated redirect preference + if (!state.currentDnslinkFqdn || !IsIpfs.ipnsUrl(state.currentTab.url)) { + // No DNSLink, reload URL as-is + await browser.tabs.reload(state.currentTab.id) + } else { + // DNSLinked websites require URL change + // from http?://gateway.tld/ipns/{fqdn}/some/path + // to http://{fqdn}/some/path + // (defaulting to http: https websites will have HSTS or a redirect) + const originalUrl = state.currentTab.url.replace(/^.*\/ipns\//, 'http://') + await browser.tabs.update(state.currentTab.id, { + // FF only: loadReplace: true, + url: originalUrl + }) + } + } catch (error) { + console.error(`Unable to update redirect state due to ${error}`) + emitter.emit('render') + } + window.close() + }) + emitter.on('toggleNodeType', async () => { const prev = state.ipfsNodeType state.ipfsNodeType = prev === 'external' ? 'embedded' : 'external' @@ -175,8 +226,7 @@ module.exports = (state, emitter) => { const prev = state.active state.active = !prev if (!state.active) { - const options = await browser.storage.local.get() - state.gatewayAddress = options.publicGatewayUrl + state.gatewayAddress = state.pubGwURLString state.ipfsApiUrl = null state.gatewayVersion = null state.swarmPeers = null @@ -188,14 +238,16 @@ module.exports = (state, emitter) => { } catch (error) { console.error(`Unable to update global Active flag due to ${error}`) state.active = prev - emitter.emit('render') } + emitter.emit('render') }) async function updatePageActionsState (status) { // Check if current page is an IPFS one state.isIpfsContext = status.ipfsPageActionsContext || false state.currentTab = status.currentTab || null + state.currentFqdn = status.currentFqdn || null + state.currentTabRedirectOptOut = state.noRedirectHostnames.includes(state.currentFqdn) // browser.pageAction-specific items that can be rendered earlier (snappy UI) requestAnimationFrame(async () => { @@ -220,21 +272,21 @@ module.exports = (state, emitter) => { async function updateBrowserActionState (status) { if (status) { - const options = await browser.storage.local.get() - state.active = status.active - if (state.active && options.useCustomGateway && (options.ipfsNodeType !== 'embedded')) { - state.gatewayAddress = options.customGatewayUrl + // Copy all attributes + Object.assign(state, status) + + if (state.active && status.redirect && (status.ipfsNodeType !== 'embedded')) { + state.gatewayAddress = status.gwURLString } else { - state.gatewayAddress = options.publicGatewayUrl + state.gatewayAddress = status.pubGwURLString } - state.ipfsNodeType = status.ipfsNodeType - state.redirectEnabled = state.active && options.useCustomGateway + state.globalRedirectEnabled = state.active && status.redirect // Upload requires access to the background page (https://github.com/ipfs-shipyard/ipfs-companion/issues/477) state.isApiAvailable = state.active && !!(await browser.runtime.getBackgroundPage()) && !browser.extension.inIncognitoContext // https://github.com/ipfs-shipyard/ipfs-companion/issues/243 state.swarmPeers = !state.active || status.peerCount === -1 ? null : status.peerCount state.isIpfsOnline = state.active && status.peerCount > -1 state.gatewayVersion = state.active && status.gatewayVersion ? status.gatewayVersion : null - state.ipfsApiUrl = state.active ? options.ipfsApiUrl : null + state.ipfsApiUrl = state.active ? status.apiURLString : null state.webuiRootUrl = status.webuiRootUrl } else { state.ipfsNodeType = 'external' diff --git a/add-on/src/popup/page-action/page.js b/add-on/src/popup/page-action/page.js index a43308e58..b94d4b16b 100644 --- a/add-on/src/popup/page-action/page.js +++ b/add-on/src/popup/page-action/page.js @@ -3,7 +3,7 @@ const html = require('choo/html') const header = require('./header') -const contextActions = require('../browser-action/context-actions') +const { contextActions } = require('../browser-action/context-actions') // Render the page-action page: // Passed current app `state` from the store and `emit`, a function to create @@ -12,7 +12,9 @@ module.exports = function pageActionPage (state, emit) { const onCopy = (copyAction) => emit('copy', copyAction) const onPin = () => emit('pin') const onUnPin = () => emit('unPin') - const contextActionsProps = Object.assign({ onCopy, onPin, onUnPin }, state) + const onToggleSiteRedirect = () => emit('toggleSiteRedirect') + + const contextActionsProps = Object.assign({ onCopy, onPin, onUnPin, onToggleSiteRedirect }, state) // Instant init: page-action is shown only in ipfsContext contextActionsProps.isIpfsContext = true diff --git a/package.json b/package.json index 3835cc0fa..43b648e66 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "bundle:brave": "shx cat add-on/manifest.common.json add-on/manifest.brave.json | json --deep-merge > add-on/manifest.json && web-ext build -a build/brave/", "watch": "npm-run-all build:copy --parallel watch:*", "watch:js": "run-p watch:js:*", - "watch:js:webpack": "webpack --watch --progress -d --devtool inline-source-map", + "watch:js:webpack": "webpack --watch --progress -d --devtool inline-source-map --config ./webpack.config.js", "test": "run-s test:*", "test:functional": " nyc --reporter=lcov --reporter=text mocha --timeout 15000 --require ignore-styles \"test/functional/**/*.test.js\"", "lint": "run-s lint:*", diff --git a/yarn.lock b/yarn.lock index 2a9d13dbd..c0979d3d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3350,11 +3350,6 @@ dicer@^0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" -diff@1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/diff/-/diff-1.0.7.tgz#24bbb001c4a7d5522169e7cabdb2c2814ed91cf4" - integrity sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ= - diff@3.5.0, diff@^3.1.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -8666,17 +8661,7 @@ mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -mocha-jenkins-reporter@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mocha-jenkins-reporter/-/mocha-jenkins-reporter-0.4.1.tgz#d944ce5f7fb157f4bbcea8de8d535db50ade2823" - integrity sha512-IqnIylrkKJG0lxeoawRkhv/uiYojMEw3o9TQOpDFarPYKVq4ymngVPwsyfMB0XMDqtDbOTOCviFg8xOLHb80/Q== - dependencies: - diff "1.0.7" - mkdirp "0.5.1" - mocha "^5.2.0" - xml "^1.0.1" - -mocha@5.2.0, mocha@^5.2.0: +mocha@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== @@ -13888,11 +13873,6 @@ xml2js@^0.4.17, xml2js@~0.4.4: sax ">=0.6.0" xmlbuilder "~9.0.1" -xml@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" - integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= - xmlbuilder@~9.0.1: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"