Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mv3): Manifest V3 Migration Checklist #1170

Merged
merged 12 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be gone.

}
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