Skip to content

Commit

Permalink
feat(mv3): Manifest V3 Migration Checklist (#1170)
Browse files Browse the repository at this point in the history
* feat(mv3): ✨ MV3 Manifest Migration

* fix(mv3): 🗑️ No longer needed

* fix(mv3): 🔧 Corresponding MV3 Changes

* feat(mv3): 📦 Adding deps

* feat(telemetry): Refactor Metrics Tracking from background service_worker (#1172)

* feat(telemetry): ♻️ Init Telemetry away from background service_worker.

* feat(telemetry): ♻️ Track metrics from page context instead of service_worker context

* feat(mv3): 🩹 Patch @protobufjs/inquire to not have eval

* fix(mv3): 👽 Fixing contextMenus API changes (#1177)

* fix(mv3): 👽 Fixing contextMenus API changes

* fix(mv3): 🩹 Fixing the browser.action api

* fix(mv3): webpack configs (#1178)

* fix(mv3): 👽 Fixing contextMenus API changes

* fix(mv3): 🩹 Fixing the browser.action api

* fix(mv3): 🔧 Fixing webpack config

* fix(mv3): 🩹 Patching debug package and making background sw work.

* feat(mv3): ✨ XHR to Fetch Migration (#1179)

* fix(mv3): 👽 Fixing contextMenus API changes

* fix(mv3): 🩹 Fixing the browser.action api

* fix(mv3): 🔧 Fixing webpack config

* fix(mv3): 🩹 Patching debug package and making background sw work.

* feat(mv3): ✨ XMLHttpRequest => fetch

* fix(mv3): 🚧 Related changes to ipfs-path

* fix(mv3): 🚧 Other Related changes

* fix(mv3): 🚧 Changes to companion

* fix(mv3): ✅ Fixing tests to account for async code.

* Fix(mv3): Popup Was Broken (#1180)

* fix(mv3): 👽 Fixing contextMenus API changes

* fix(mv3): 🩹 Fixing the browser.action api

* fix(mv3): 🔧 Fixing webpack config

* fix(mv3): 🩹 Patching debug package and making background sw work.

* feat(mv3): ✨ XMLHttpRequest => fetch

* fix(mv3): 🚧 Related changes to ipfs-path

* fix(mv3): 🚧 Other Related changes

* fix(mv3): 🚧 Changes to companion

* fix(mv3): ✅ Fixing tests to account for async code.

* feat(mv3): ♻️ Implementing a non-windowed companion instance

* fix(mv3): 🗑️ Removing calls to background page.

* fix: 🗑️ Unneeded debug statement

* fix(mv3): 🛂 Limiting permissions to chrome-extension

* Update add-on/src/lib/ipfs-companion.js

Co-authored-by: Russell Dempsey <[email protected]>

---------

Co-authored-by: Russell Dempsey <[email protected]>
  • Loading branch information
whizzzkid and SgtPooki authored Mar 24, 2023
1 parent e57f0bf commit 1ea8b09
Show file tree
Hide file tree
Showing 31 changed files with 4,395 additions and 596 deletions.
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v18.14.0
11 changes: 5 additions & 6 deletions add-on/manifest.chromium.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
{
"minimum_chrome_version": "72",
"permissions": [
"<all_urls>",
"clipboardWrite",
"contextMenus",
"idle",
"tabs",
"notifications",
"storage",
"tabs",
"unlimitedStorage",
"contextMenus",
"clipboardWrite",
"webNavigation",
"webRequest",
"webRequestBlocking"
"webRequest"
],
"host_permissions": ["<all_urls>"],
"incognito": "not_allowed"
}
33 changes: 21 additions & 12 deletions add-on/manifest.common.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifest_version": 2,
"manifest_version": 3,
"name": "__MSG_manifest_extensionName__",
"short_name": "__MSG_manifest_shortExtensionName__",
"version": "2.22.1",
Expand All @@ -12,9 +12,9 @@
"128": "icons/png/ipfs-logo-on_128.png"
},
"background": {
"page": "dist/background/background.html"
"service_worker": "dist/bundles/backgroundPage.bundle.js"
},
"browser_action": {
"action": {
"default_icon": {
"19": "icons/png/ipfs-logo-off_19.png",
"38": "icons/png/ipfs-logo-off_38.png",
Expand All @@ -29,15 +29,24 @@
"page": "dist/options/options.html"
},
"web_accessible_resources": [
"icons/png/ipfs-logo-off_19.png",
"icons/png/ipfs-logo-off_38.png",
"icons/png/ipfs-logo-off_128.png",
"icons/ipfs-logo-on.svg",
"icons/ipfs-logo-off.svg",
"dist/recovery/recovery.css",
"dist/recovery/recovery.html",
"dist/recovery/recovery.js"
{
"resources": [
"icons/png/ipfs-logo-off_19.png",
"icons/png/ipfs-logo-off_38.png",
"icons/png/ipfs-logo-off_128.png",
"icons/ipfs-logo-on.svg",
"icons/ipfs-logo-off.svg",
"dist/recovery/recovery.css",
"dist/recovery/recovery.html",
"dist/recovery/recovery.js"
],
"matches": [
"chrome-extension://*/*"
]
}
],
"content_security_policy": "script-src 'self'; object-src 'self'; frame-src 'self';",
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'; frame-src 'self';"
},
"default_locale": "en"
}
2 changes: 1 addition & 1 deletion add-on/manifest.firefox.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"browser_action": {
"action": {
"browser_style": false
},
"options_ui": {
Expand Down
5 changes: 0 additions & 5 deletions add-on/src/background/background.html

This file was deleted.

20 changes: 5 additions & 15 deletions add-on/src/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,15 @@
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import createIpfsCompanion from '../lib/ipfs-companion.js'
import { onInstalled } from '../lib/on-installed.js'
import { getUninstallURL } from '../lib/on-uninstalled.js'
import { optionDefaults } from '../lib/options.js'
import createIpfsCompanion from '../lib/ipfs-companion.js'

// register lifecycle hooks early, otherwise we miss first install event
browser.runtime.onInstalled.addListener(onInstalled)
browser.runtime.setUninstallURL(getUninstallURL(browser))

// init add-on after all libs are loaded
document.addEventListener('DOMContentLoaded', async () => {
browser.runtime.sendMessage({ telemetry: { trackView: 'background' } })
// setting debug namespaces require page reload to get applied
const debugNs = (await browser.storage.local.get({ logNamespaces: optionDefaults.logNamespaces })).logNamespaces
if (debugNs !== localStorage.debug) {
localStorage.debug = debugNs
window.location.reload()
}
// init inlined to read updated localStorage.debug
// @ts-expect-error - TS does not know about window.ipfsCompanion
window.ipfsCompanion = await createIpfsCompanion()
})
const init = async () => {
await createIpfsCompanion()
}
init();
4 changes: 3 additions & 1 deletion add-on/src/landing-pages/welcome/store.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'
/* eslint-env browser, webextensions */
import browser from 'webextension-polyfill'
import { handleConsentFromState, trackView } from '../../lib/telemetry.js'

export default function createWelcomePageStore (i18n, runtime) {
return function welcomePageStore (state, emitter) {
Expand All @@ -9,7 +10,8 @@ export default function createWelcomePageStore (i18n, runtime) {
state.webuiRootUrl = null
let port
emitter.on('DOMContentLoaded', async () => {
browser.runtime.sendMessage({ telemetry: { trackView: 'welcome' } })
handleConsentFromState(state)
trackView('welcome')
emitter.emit('render')
port = runtime.connect({ name: 'browser-action-port' })
port.onMessage.addListener(async (message) => {
Expand Down
41 changes: 6 additions & 35 deletions add-on/src/lib/context-menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,29 +71,13 @@ export function createContextMenus (
getState, _runtime, ipfsPathValidator, { onAddFromContext, onCopyRawCid, onCopyAddressAtPublicGw }) {
try {
const createSubmenu = (id, contextType, menuBuilder) => {
browser.contextMenus.create({
id,
title: browser.i18n.getMessage(id),
documentUrlPatterns: ['<all_urls>'],
contexts: [contextType]
})
browser.contextMenus.onClicked.addListener((...args) => console.log(args))
}
const createImportToIpfsMenuItem = (parentId, id, contextType, ipfsAddOptions) => {
const itemId = `${parentId}_${id}`
apiMenuItems.add(itemId)
return browser.contextMenus.create({
id: itemId,
parentId,
title: browser.i18n.getMessage(id),
contexts: [contextType],
documentUrlPatterns: ['<all_urls>'],
enabled: false,
/* no support for 'icons' in Chrome
icons: {
'48': '/ui-kit/icons/stroke_cube.svg'
}, */
onclick: (context) => onAddFromContext(context, contextType, ipfsAddOptions)
})
return browser.contextMenus.onClicked.addListener((context) => onAddFromContext(context, contextType, ipfsAddOptions)
)
}
const createCopierMenuItem = (parentId, id, contextType, handler) => {
const itemId = `${parentId}_${id}`
Expand All @@ -102,22 +86,9 @@ export function createContextMenus (
if (apiMenuItemIds.has(id)) {
apiMenuItems.add(itemId)
}
return browser.contextMenus.create({
id: itemId,
parentId,
title: browser.i18n.getMessage(id),
contexts: [contextType],
documentUrlPatterns: [
'*://*/ipfs/*', '*://*/ipns/*',
'*://*.ipfs.dweb.link/*', '*://*.ipns.dweb.link/*', // TODO: add any custom public gateway from Preferences
'*://*.ipfs.localhost/*', '*://*.ipns.localhost/*'
],
/* no support for 'icons' in Chrome
icons: {
'48': '/ui-kit/icons/stroke_copy.svg'
}, */
onclick: (context) => handler(context, contextType)
})
return browser.contextMenus.onClicked.addListener(
(context) => handler(context, contextType)
)
}
const buildSubmenu = (parentId, contextType) => {
createSubmenu(parentId, contextType)
Expand Down
2 changes: 1 addition & 1 deletion add-on/src/lib/copier.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default function createCopier (notify, ipfsPathValidator) {

async copyAddressAtPublicGw (context, contextType) {
const url = await findValueForContext(context, contextType)
const publicUrl = ipfsPathValidator.resolveToPublicUrl(url)
const publicUrl = await ipfsPathValidator.resolveToPublicUrl(url)
await copyTextToClipboard(publicUrl, notify)
},

Expand Down
43 changes: 22 additions & 21 deletions add-on/src/lib/dnslink.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ export default function createDnslinkResolver (getState) {
!sameGateway(requestUrl, state.gwURL)
},

dnslinkAtGateway (url, dnslink) {
async dnslinkAtGateway (url, dnslink) {
if (typeof url === 'string') {
url = new URL(url)
}
if (dnslinkResolver.canRedirectToIpns(url, dnslink)) {
if (await dnslinkResolver.canRedirectToIpns(url, dnslink)) {
const state = getState()
// redirect to IPNS and leave it up to the gateway
// to load the correct path from IPFS
Expand All @@ -65,12 +65,12 @@ export default function createDnslinkResolver (getState) {
}
},

readAndCacheDnslink (fqdn) {
async readAndCacheDnslink (fqdn) {
let dnslink = dnslinkResolver.cachedDnslink(fqdn)
if (typeof dnslink === 'undefined') {
try {
log(`dnslink cache miss for '${fqdn}', running DNS TXT lookup`)
dnslink = dnslinkResolver.readDnslinkFromTxtRecord(fqdn)
dnslink = await dnslinkResolver.readDnslinkFromTxtRecord(fqdn)
if (dnslink) {
// TODO: set TTL as maxAge: setDnslink(fqdn, dnslink, maxAge)
dnslinkResolver.setDnslink(fqdn, dnslink)
Expand All @@ -96,6 +96,7 @@ export default function createDnslinkResolver (getState) {
const cachedResult = dnslinkResolver.cachedDnslink(fqdn)
if (cachedResult) return cachedResult
return lookupQueue.add(() => {
// this will resolve eventually.
return dnslinkResolver.readAndCacheDnslink(fqdn)
})
},
Expand All @@ -120,7 +121,7 @@ export default function createDnslinkResolver (getState) {
},

// low level lookup without cache
readDnslinkFromTxtRecord (fqdn) {
async readDnslinkFromTxtRecord (fqdn) {
const state = getState()
let apiProvider
if (!state.ipfsNodeType.startsWith('embedded') && state.peerCount !== offlinePeerCount) {
Expand All @@ -139,29 +140,29 @@ export default function createDnslinkResolver (getState) {
// TODO: revisit after https://github.com/ipfs/js-ipfs-api/issues/501 is addressed
// TODO: consider worst-case-scenario fallback to https://developers.google.com/speed/public-dns/docs/dns-over-https
const apiCall = `${apiProvider}api/v0/name/resolve/${fqdn}?r=false`
const xhr = new XMLHttpRequest() // older XHR API us used because window.fetch appends Origin which causes error 403 in go-ipfs
// synchronous mode with small timeout
// (it is okay, because we do it only once, then it is cached and read via readAndCacheDnslink)
xhr.open('GET', apiCall, false)
xhr.setRequestHeader('Accept', 'application/json')
xhr.send(null)
if (xhr.status === 200) {
const dnslink = JSON.parse(xhr.responseText).Path
// console.log('readDnslinkFromTxtRecord', readDnslinkFromTxtRecord)
const response = await fetch(apiCall, {
method: 'GET',
headers: {
Accept: 'application/json'
}
})

if (response.ok) {
const { Path: dnslink } = await response.json()
if (!IsIpfs.path(dnslink)) {
throw new Error(`dnslink for '${fqdn}' is not a valid IPFS path: '${dnslink}'`)
}
return dnslink
} else if (xhr.status === 500) {
} else if (response.status === 500) {
// go-ipfs returns 500 if host has no dnslink or an error occurred
// TODO: find/fill an upstream bug to make this more intuitive
return false
} else {
throw new Error(xhr.statusText)
throw new Error(response.statusText)
}
},

canRedirectToIpns (url, dnslink) {
async canRedirectToIpns (url, dnslink) {
if (typeof url === 'string') {
url = new URL(url)
}
Expand All @@ -185,7 +186,7 @@ export default function createDnslinkResolver (getState) {
// is found in initial response.
// More: https://github.com/ipfs-shipyard/ipfs-companion/blob/master/docs/dnslink.md
const foundDnslink = dnslink ||
(getState().dnslinkPolicy === 'enabled'
await (getState().dnslinkPolicy === 'enabled'
? dnslinkResolver.readAndCacheDnslink(fqdn)
: dnslinkResolver.cachedDnslink(fqdn))
if (foundDnslink) {
Expand All @@ -205,7 +206,7 @@ export default function createDnslinkResolver (getState) {
// Test if URL contains a valid DNSLink FQDN
// in url.hostname OR in url.pathname (/ipns/<fqdn>)
// and return matching FQDN if present
findDNSLinkHostname (url) {
async findDNSLinkHostname (url) {
if (!url) return
// Normalize subdomain and path gateways to to /ipns/<fqdn>
const contentPath = ipfsContentPath(url)
Expand All @@ -214,14 +215,14 @@ export default function createDnslinkResolver (getState) {
const ipnsRoot = contentPath.match(/^\/ipns\/([^/]+)/)[1]
// console.log('findDNSLinkHostname ==> inspecting IPNS root', ipnsRoot)
// Ignore PeerIDs, match DNSLink only
if (!IsIpfs.cid(ipnsRoot) && dnslinkResolver.readAndCacheDnslink(ipnsRoot)) {
if (!IsIpfs.cid(ipnsRoot) && await dnslinkResolver.readAndCacheDnslink(ipnsRoot)) {
// console.log('findDNSLinkHostname ==> found DNSLink for FQDN in url.pathname: ', ipnsRoot)
return ipnsRoot
}
}
// Check main hostname
const { hostname } = new URL(url)
if (dnslinkResolver.readAndCacheDnslink(hostname)) {
if (await dnslinkResolver.readAndCacheDnslink(hostname)) {
// console.log('findDNSLinkHostname ==> found DNSLink for url.hostname', hostname)
return hostname
}
Expand Down
Loading

0 comments on commit 1ea8b09

Please sign in to comment.