diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 047bd360e..e762d3bb2 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -91,6 +91,10 @@ "message": "Copy Public Gateway URL", "description": "A menu item in Browser Action pop-up and right-click context menu (panel_copyCurrentPublicGwUrl)" }, + "panel_contextMenuViewOnGateway": { + "message": "View on Gateway", + "description": "A menu item in Browser Action pop-up and right-click context menu (panel_contextMenuViewOnGateway)" + }, "pageAction_titleIpfsAtPublicGateway": { "message": "IPFS resource loaded via Public Gateway", "description": "A tooltip displayed over Page Action in location bar (pageAction_titleIpfsAtPublicGateway)" @@ -275,6 +279,18 @@ "message": "Redirect requests for IPFS resources to the Custom gateway", "description": "An option description on the Preferences screen (option_useCustomGateway_description)" }, + "option_dnslinkRedirect_title": { + "message": "Force page load from custom gateway", + "description": "An option title on the Preferences screen (option_dnslinkRedirect_title)" + }, + "option_dnslinkRedirect_description": { + "message": "If global redirect is enabled, this will include DNSLink websites and redirect them to respective /ipns/{fqdn} paths at Custom Gateway", + "description": "An option description on the Preferences screen (option_dnslinkRedirect_description)" + }, + "option_dnslinkRedirect_warning": { + "message": "Redirecting to a path-based gateway breaks Origin-based security isolation of DNSLink website! Please leave this disabled unless you are aware of (and ok with) related risks.", + "description": "A warning on the Preferences screen, displayed when URL does not belong to Secure Context (option_customGatewayUrl_warning)" + }, "option_noRedirectHostnames_title": { "message": "Redirect Opt-Outs", "description": "An option title on the Preferences screen (option_noRedirectHostnames_title)" @@ -327,6 +343,10 @@ "message": "Toggle use of Custom Gateway when IPFS API availability changes", "description": "An option description on the Preferences screen (option_automaticMode_description)" }, + "option_header_dnslink": { + "message": "DNSLink", + "description": "A section header on the Preferences screen (option_header_dnslink)" + }, "option_header_experiments": { "message": "Experiments", "description": "A section header on the Preferences screen (option_header_experiments)" @@ -364,11 +384,11 @@ "description": "An option description on the Preferences screen (option_linkify_description)" }, "option_dnslinkPolicy_title": { - "message": "DNSLink Support", + "message": "DNSLink Lookup", "description": "An option title on the Preferences screen (option_dnslinkPolicy_title)" }, "option_dnslinkPolicy_description": { - "message": "Select DNS TXT lookup policy to load IPFS hosted sites over IPFS where possible", + "message": "Lookup policy for displaying context actions on websites with DNSLink", "description": "An option description on the Preferences screen (option_dnslinkPolicy_description)" }, "option_dnslinkPolicy_disabled": { diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index bf78c4bd4..f8f4d9e7c 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -58,9 +58,11 @@ const contextMenuImportToIpfsSelection = 'contextMenu_importToIpfsSelection' const contextMenuCopyCanonicalAddress = 'panelCopy_currentIpfsAddress' const contextMenuCopyRawCid = 'panelCopy_copyRawCid' const contextMenuCopyAddressAtPublicGw = 'panel_copyCurrentPublicGwUrl' +const contextMenuViewOnGateway = 'panel_contextMenuViewOnGateway' module.exports.contextMenuCopyCanonicalAddress = contextMenuCopyCanonicalAddress module.exports.contextMenuCopyRawCid = contextMenuCopyRawCid module.exports.contextMenuCopyAddressAtPublicGw = contextMenuCopyAddressAtPublicGw +module.exports.contextMenuViewOnGateway = contextMenuViewOnGateway // menu items that are enabled only when API is online const apiMenuItems = new Set() diff --git a/add-on/src/lib/inspector.js b/add-on/src/lib/inspector.js new file mode 100644 index 000000000..3226c1e36 --- /dev/null +++ b/add-on/src/lib/inspector.js @@ -0,0 +1,21 @@ +'use strict' + +const browser = require('webextension-polyfill') +const { findValueForContext } = require('./context-menus') +const { pathAtHttpGateway } = require('./ipfs-path') + +function createInspector (notify, ipfsPathValidator, getState) { + return { + async viewOnGateway (context, contextType) { + const url = await findValueForContext(context, contextType) + const ipfsPath = ipfsPathValidator.resolveToIpfsPath(url) + const gateway = getState().pubGwURLString + const gatewayUrl = pathAtHttpGateway(ipfsPath, gateway) + await browser.tabs.create({ url: gatewayUrl }) + } + // TODO: view in WebUI's Files + // TODO: view in WebUI's IPLD Explorer + } +} + +module.exports = createInspector diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 759a53222..9f21bbb68 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -18,8 +18,9 @@ const { createIpfsUrlProtocolHandler } = require('./ipfs-protocol') const createIpfsImportHandler = require('./ipfs-import') const createNotifier = require('./notifier') const createCopier = require('./copier') +const createInspector = require('./inspector') const { createRuntimeChecks } = require('./runtime-checks') -const { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('./context-menus') +const { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress, contextMenuViewOnGateway } = require('./context-menus') const createIpfsProxy = require('./ipfs-proxy') const { showPendingLandingPages } = require('./on-installed') @@ -34,6 +35,7 @@ module.exports = async function init () { var modifyRequest var notify var copier + var inspector var runtime var contextMenus var apiStatusUpdateInterval @@ -69,6 +71,7 @@ module.exports = async function init () { ipfsPathValidator = createIpfsPathValidator(getState, getIpfs, dnslinkResolver) ipfsImportHandler = createIpfsImportHandler(getState, getIpfs, ipfsPathValidator, runtime) copier = createCopier(notify, ipfsPathValidator) + inspector = createInspector(notify, ipfsPathValidator, getState) contextMenus = createContextMenus(getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress: copier.copyCanonicalAddress, @@ -212,6 +215,7 @@ module.exports = async function init () { const BrowserActionMessageHandlers = { notification: (message) => notify(message.title, message.message), + [contextMenuViewOnGateway]: inspector.viewOnGateway, [contextMenuCopyCanonicalAddress]: copier.copyCanonicalAddress, [contextMenuCopyRawCid]: copier.copyRawCid, [contextMenuCopyAddressAtPublicGw]: copier.copyAddressAtPublicGw @@ -676,6 +680,7 @@ module.exports = async function init () { case 'preloadAtPublicGateway': case 'openViaWebUI': case 'noRedirectHostnames': + case 'dnslinkRedirect': state[key] = change.newValue break } diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index 28ae1c2f8..76fb317f3 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -142,7 +142,7 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru return redirectToGateway(request.url, state, ipfsPathValidator) } // Detect dnslink using heuristics enabled in Preferences - if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) { + if (state.dnslinkRedirect && state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) { const dnslinkRedirect = dnslinkResolver.dnslinkRedirect(request.url) if (dnslinkRedirect && isSafeToRedirect(request, runtime)) { // console.log('onBeforeRequest.dnslinkRedirect', dnslinkRedirect) @@ -339,10 +339,9 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru if (header.name.toLowerCase() === 'x-ipfs-path' && isSafeToRedirect(request, runtime)) { // console.log('onHeadersReceived.request.responseHeaders', request.responseHeaders.length) const xIpfsPath = header.value - log(`detected x-ipfs-path for ${request.url}: ${xIpfsPath}`) // First: Check if dnslink heuristic yields any results // Note: this depends on which dnslink lookup policy is selecten in Preferences - if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) { + if (state.dnslinkRedirect && state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) { // x-ipfs-path is a strong indicator of IPFS support // so we force dnslink lookup to pre-populate dnslink cache // in a way that works even when state.dnslinkPolicy !== 'enabled' @@ -358,7 +357,7 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru if (IsIpfs.ipnsPath(xIpfsPath)) { // Ignore unhandled IPNS path by this point // (means DNSLink is disabled so we don't want to make a redirect that works like DNSLink) - log(`onHeadersReceived: ignoring x-ipfs-path=${xIpfsPath} (dnslinkPolicy=false or missing DNS TXT record)`) + // log(`onHeadersReceived: ignoring x-ipfs-path=${xIpfsPath} (dnslinkRedirect=false, dnslinkPolicy=false or missing DNS TXT record)`) } else if (IsIpfs.ipfsPath(xIpfsPath)) { // It is possible that someone exposed /ipfs// under / // and our path-based onBeforeRequest heuristics were unable diff --git a/add-on/src/lib/options.js b/add-on/src/lib/options.js index dfc31076a..3dc5dad41 100644 --- a/add-on/src/lib/options.js +++ b/add-on/src/lib/options.js @@ -17,6 +17,7 @@ exports.optionDefaults = Object.freeze({ automaticMode: true, linkify: false, dnslinkPolicy: 'best-effort', + dnslinkRedirect: false, recoverFailedHttpRequests: true, detectIpfsPathHeader: true, preloadAtPublicGateway: true, diff --git a/add-on/src/options/forms/dnslink-form.js b/add-on/src/options/forms/dnslink-form.js new file mode 100644 index 000000000..dfbf77399 --- /dev/null +++ b/add-on/src/options/forms/dnslink-form.js @@ -0,0 +1,70 @@ +'use strict' +/* eslint-env browser, webextensions */ + +const browser = require('webextension-polyfill') +const html = require('choo/html') +const switchToggle = require('../../pages/components/switch-toggle') + +function dnslinkForm ({ + dnslinkPolicy, + dnslinkRedirect, + onOptionChange +}) { + const onDnslinkPolicyChange = onOptionChange('dnslinkPolicy') + const onDnslinkRedirectChange = onOptionChange('dnslinkRedirect') + + return html` +
+
+ ${browser.i18n.getMessage('option_header_dnslink')} +
+ + +
+
+ +
${switchToggle({ id: 'dnslinkRedirect', checked: dnslinkRedirect, onchange: onDnslinkRedirectChange })}
+
+
+
+ ` +} + +module.exports = dnslinkForm diff --git a/add-on/src/options/forms/experiments-form.js b/add-on/src/options/forms/experiments-form.js index c5e2a5e95..74150d50e 100644 --- a/add-on/src/options/forms/experiments-form.js +++ b/add-on/src/options/forms/experiments-form.js @@ -9,7 +9,6 @@ function experimentsForm ({ displayNotifications, catchUnhandledProtocols, linkify, - dnslinkPolicy, recoverFailedHttpRequests, detectIpfsPathHeader, ipfsProxy, @@ -20,7 +19,6 @@ function experimentsForm ({ const onDisplayNotificationsChange = onOptionChange('displayNotifications') const onCatchUnhandledProtocolsChange = onOptionChange('catchUnhandledProtocols') const onLinkifyChange = onOptionChange('linkify') - const onDnslinkPolicyChange = onOptionChange('dnslinkPolicy') const onrecoverFailedHttpRequestsChange = onOptionChange('recoverFailedHttpRequests') const onDetectIpfsPathHeaderChange = onOptionChange('detectIpfsPathHeader') const onIpfsProxyChange = onOptionChange('ipfsProxy') @@ -66,36 +64,6 @@ function experimentsForm ({
${switchToggle({ id: 'linkify', checked: linkify, onchange: onLinkifyChange })}
-
- - -