Skip to content

Commit 4ed94bf

Browse files
committed
feat: normalize dweb: in href and src
Content script detects IPFS-related protocols in `href` and `src` attributes of Elements such as `<a>` or `<img>` and replaces them with URL at the user-specified public HTTP gateway. Note that if IPFS API is online, HTTP request will be redirected to custom gateway. Closes #286
1 parent 7759546 commit 4ed94bf

File tree

2 files changed

+117
-2
lines changed

2 files changed

+117
-2
lines changed

add-on/src/lib/common.js

+24-2
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,8 @@ function isIpfsPageActionsContext (url) {
460460
}
461461

462462
async function onUpdatedTab (tabId, changeInfo, tab) {
463-
if (tab && tab.url && !tab.url.startsWith('chrome://')) {
464-
if (state.linkify && changeInfo.status === 'complete') {
463+
if (changeInfo.status === 'complete' && tab.url && tab.url.startsWith('http')) {
464+
if (state.linkify) {
465465
console.log(`[ipfs-companion] Running linkfyDOM for ${tab.url}`)
466466
try {
467467
await browser.tabs.executeScript(tabId, {
@@ -480,6 +480,28 @@ async function onUpdatedTab (tabId, changeInfo, tab) {
480480
console.error(`Unable to linkify DOM at '${tab.url}' due to`, error)
481481
}
482482
}
483+
if (state.catchUnhandledProtocols) {
484+
// console.log(`[ipfs-companion] Normalizing links with unhandled protocols at ${tab.url}`)
485+
// See: https://github.com/ipfs/ipfs-companion/issues/286
486+
try {
487+
// pass the URL of user-preffered public gateway
488+
await browser.tabs.executeScript(tabId, {
489+
code: `window.ipfsCompanionPubGwURL = '${state.pubGwURLString}'`,
490+
matchAboutBlank: false,
491+
allFrames: true,
492+
runAt: 'document_start'
493+
})
494+
// inject script that normalizes `href` and `src` containing unhandled protocols
495+
await browser.tabs.executeScript(tabId, {
496+
file: '/src/lib/normalizeLinksWithUnhandledProtocols.js',
497+
matchAboutBlank: false,
498+
allFrames: true,
499+
runAt: 'document_end'
500+
})
501+
} catch (error) {
502+
console.error(`Unable to normalize links at '${tab.url}' due to`, error)
503+
}
504+
}
483505
}
484506
}
485507

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict'
2+
/* eslint-env browser, webextensions */
3+
4+
/*
5+
* This content script detects IPFS-related protocols in `href` and `src`
6+
* attributes and replaces them with URL at the user-specified public HTTP gateway.
7+
* Note that if IPFS API is online, HTTP request will be redirected to custom gateway.
8+
*
9+
* For more background see: https://github.com/ipfs/ipfs-companion/issues/286
10+
*
11+
* Test page: http://bit.ly/2hXiuUz
12+
*/
13+
14+
;(function (alreadyLoaded) {
15+
if (alreadyLoaded) {
16+
return
17+
}
18+
19+
// Limit contentType to "text/plain" or "text/html"
20+
if (document.contentType !== undefined && document.contentType !== 'text/plain' && document.contentType !== 'text/html') {
21+
return
22+
}
23+
24+
// prevent double init
25+
window.ipfsCompanionNormalizedUnhandledProtocols = true
26+
27+
// XPath selects all elements that have `href` of `src` attribute starting with one of IPFS-related protocols
28+
const xpath = ".//*[starts-with(@href, 'ipfs://') or starts-with(@href, 'ipns://') or starts-with(@href, 'dweb:') " +
29+
" or starts-with(@src, 'ipfs://') or starts-with(@src, 'ipns://') or starts-with(@src, 'dweb:')]"
30+
31+
const pubGwURL = window.ipfsCompanionPubGwURL
32+
33+
function init () {
34+
// initial run
35+
normalizeTree(document.body)
36+
37+
// listen for future DOM changes
38+
new MutationObserver(function (mutations) {
39+
mutations.forEach(function (mutation) {
40+
if (mutation.type === 'childList') {
41+
for (let addedNode of mutation.addedNodes) {
42+
if (addedNode.nodeType === Node.ELEMENT_NODE) {
43+
setTimeout(() => normalizeTree(addedNode), 0)
44+
}
45+
}
46+
}
47+
})
48+
}).observe(document.body, {
49+
characterData: false,
50+
childList: true,
51+
subtree: true
52+
})
53+
}
54+
55+
function normalizeElement (element) {
56+
if (element.href) {
57+
// console.log('normalizeElement.href: ' + element.href)
58+
element.href = normalizeAddress(element.href)
59+
} else if (element.src) {
60+
// console.log('normalizeElement.src: ' + element.src)
61+
element.src = normalizeAddress(element.src)
62+
}
63+
}
64+
65+
// replaces unhandled protocol with a regular URL at a public gateway
66+
function normalizeAddress (addr) {
67+
return addr
68+
.replace(/^dweb:\//i, pubGwURL) // dweb:/ipfs/Qm → /ipfs/Qm
69+
.replace(/^ipfs:\/\//i, `${pubGwURL}ipfs/`) // ipfs://Qm → /ipfs/Qm
70+
.replace(/^ipns:\/\//i, `${pubGwURL}ipns/`) // ipns://Qm → /ipns/Qm
71+
}
72+
73+
function normalizeTree (root) {
74+
const xpathResult = document.evaluate(xpath, root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
75+
let i = 0
76+
function continuation () {
77+
let node = null
78+
let counter = 0
79+
while ((node = xpathResult.snapshotItem(i++))) {
80+
const parent = node.parentNode
81+
// Skip if no longer in visible DOM
82+
if (!parent || !document.body.contains(node)) continue
83+
normalizeElement(node)
84+
if (++counter > 10) {
85+
return setTimeout(continuation, 0)
86+
}
87+
}
88+
}
89+
window.requestAnimationFrame(continuation)
90+
}
91+
92+
init()
93+
}(window.ipfsCompanionNormalizedUnhandledProtocols))

0 commit comments

Comments
 (0)