diff --git a/add-on/manifest.firefox.json b/add-on/manifest.firefox.json index 0865fb696..a57e25cc9 100644 --- a/add-on/manifest.firefox.json +++ b/add-on/manifest.firefox.json @@ -18,6 +18,7 @@ "default_title": "__MSG_pageAction_titleNonIpfs__", "default_popup": "dist/popup/page-action/index.html" }, + "content_scripts": [ ], "protocol_handlers": [ { "protocol": "web+dweb", diff --git a/add-on/src/contentScripts/ipfs-proxy/content.js b/add-on/src/contentScripts/ipfs-proxy/content.js index b98536d46..d43f0b5ee 100644 --- a/add-on/src/contentScripts/ipfs-proxy/content.js +++ b/add-on/src/contentScripts/ipfs-proxy/content.js @@ -7,14 +7,12 @@ const injectScript = require('./inject-script') function init () { const port = browser.runtime.connect({ name: 'ipfs-proxy' }) - // Forward on messages from background to the page and vice versa port.onMessage.addListener((data) => { if (data && data.sender && data.sender.startsWith('postmsg-rpc/')) { window.postMessage(data, '*') } }) - window.addEventListener('message', (msg) => { if (msg.data && msg.data.sender && msg.data.sender.startsWith('postmsg-rpc/')) { port.postMessage(msg.data) @@ -24,8 +22,30 @@ function init () { injectScript(rawCode) } -// Restricting window.ipfs to Secure Context -// See: https://github.com/ipfs-shipyard/ipfs-companion/issues/476 -if (window.isSecureContext) { +function injectIpfsProxy () { + // Skip if proxy is already present + if (window.ipfs) { + return false + } + // Restricting window.ipfs to Secure Context + // See: https://github.com/ipfs-shipyard/ipfs-companion/issues/476 + if (!window.isSecureContext) { + return false + } + // Skip if not in HTML context + // Check 1/2 + const doctype = window.document.doctype + if (doctype && doctype.name !== 'html') { + return false + } + // Check 2/2 + if (document.documentElement.nodeName !== 'HTML') { + return false + } + // Should be ok by now + return true +} + +if (injectIpfsProxy()) { init() } diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 64b04d769..439e853b2 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -30,6 +30,7 @@ module.exports = async function init () { var contextMenus var apiStatusUpdateInterval var ipfsProxy + var ipfsProxyContentScript const idleInSecs = 5 * 60 const browserActionPortName = 'browser-action-port' @@ -60,6 +61,7 @@ module.exports = async function init () { }) modifyRequest = createRequestModifier(getState, dnsLink, ipfsPathValidator, runtime) ipfsProxy = createIpfsProxy(() => ipfs, getState) + ipfsProxyContentScript = await registerIpfsProxyContentScript() registerListeners() await setApiStatusUpdateInterval(options.ipfsApiPollMs) await storeMissingOptions( @@ -95,6 +97,33 @@ module.exports = async function init () { } } + // Register Content Script responsible for loading window.ipfs (ipfsProxy) + // + // The key difference between tabs.executeScript and contentScripts API + // is the latter provides guarantee to execute before anything else. + // https://github.com/ipfs-shipyard/ipfs-companion/issues/451#issuecomment-382669093 + async function registerIpfsProxyContentScript (previousHandle) { + previousHandle = previousHandle || ipfsProxyContentScript + if (previousHandle) { + previousHandle.unregister() + } + if (!state.ipfsProxy || !browser.contentScripts) { + // no-op if window.ipfs is disabled in Preferences + // or if runtime has no contentScript API + // (Chrome loads content script via manifest) + return + } + const newHandle = await browser.contentScripts.register({ + matches: [''], + js: [ + {file: '/dist/bundles/ipfsProxyContentScript.bundle.js'} + ], + allFrames: true, + runAt: 'document_start' + }) + return newHandle + } + // HTTP Request Hooks // =================================================================== @@ -544,13 +573,16 @@ module.exports = async function init () { case 'useCustomGateway': state.redirect = change.newValue break + case 'ipfsProxy': + state[key] = change.newValue + ipfsProxyContentScript = await registerIpfsProxyContentScript() + break case 'linkify': case 'catchUnhandledProtocols': case 'displayNotifications': case 'automaticMode': case 'dnslink': case 'preloadAtPublicGateway': - case 'ipfsProxy': state[key] = change.newValue break } @@ -616,6 +648,10 @@ module.exports = async function init () { contextMenus = null ipfsProxy.destroy() ipfsProxy = null + if (ipfsProxyContentScript) { + ipfsProxyContentScript.unregister() + ipfsProxyContentScript = null + } await destroyIpfsClient() } }