upgrade browser extension to manifest v3

Chrome's and the Chrome Web Store's push for [Manifest V3](https://developer.chrome.com/docs/extensions/develop/migrate) means that our browser extension will start to be unusable in a few months unless we upgrade to Manifest V3.

- Removes "Enable Sourcegraph on this domain" context menu item that didn't seem to work. We were adding this ourselves (using `webext-domain-permission-toggle`), and it didn't seem to work. Our options popup shows a permission request, which is sufficient.
- Otherwise preserves all existing functionality as best I could tell.

Test plan:

1. Confirm that the browser extension continues to inject the Sourcegraph icon on https://github.com/hashicorp/errwrap.
1. Load up https://gitlab.com/sqs/web in my local dev Sourcegraph instance and confirm that the browser extension injects the Sourcegraph icon on https://gitlab.com/sqs/web code files.
This commit is contained in:
Quinn Slack 2024-07-19 04:26:15 -07:00
parent 341bfe749f
commit 8fb7f19310
7 changed files with 83 additions and 78 deletions

View File

@ -262,7 +262,6 @@ ts_project(
"//:node_modules/utility-types",
"//:node_modules/uuid",
"//:node_modules/vitest",
"//:node_modules/webext-domain-permission-toggle",
"//:node_modules/webextension-polyfill", #keep
],
)

View File

@ -1,16 +1,17 @@
{
"$schema": "http://json.schemastore.org/webextension",
"$schema": "https://json.schemastore.org/chrome-manifest",
"version": "0.0.0",
"name": "Sourcegraph",
"manifest_version": 2,
"manifest_version": 3,
"description": "Adds code intelligence to GitHub, GitLab, and other hosts: hovers, definitions, references. For 20+ languages.",
"browser_action": {
"action": {
"default_title": "Sourcegraph",
"default_icon": {
"32": "img/icon-32.png",
"48": "img/icon-48.png",
"128": "img/icon-128.png"
}
},
"default_popup": "options.html"
},
"icons": {
"32": "img/icon-32.png",
@ -18,8 +19,18 @@
"128": "img/icon-128.png"
},
"background": {
"scripts": ["js/backgroundPage.main.bundle.js"]
"service_worker": "js/backgroundPage.main.bundle.js"
},
"content_scripts": [
{
"matches": ["https://github.com/*"],
"js": ["js/contentPage.main.bundle.js"],
"run_at": "document_idle"
}
],
"permissions": ["activeTab", "storage"],
"host_permissions": ["https://github.com/*", "https://sourcegraph.com/*"],
"optional_host_permissions": ["https://*/*", "http://*/*"],
"options_ui": {
"page": "options.html",
"open_in_tab": true
@ -27,31 +38,21 @@
"storage": {
"managed_schema": "schema.json"
},
"optional_permissions": ["tabs", "http://*/*", "https://*/*"],
"content_security_policy": "script-src 'self' blob:; object-src 'self'",
"web_accessible_resources": ["img/*", "css/*"],
"web_accessible_resources": [
{
"resources": ["img/*", "css/*"],
"matches": ["<all_urls>"]
}
],
"omnibox": {
"keyword": "src"
},
"applications": {
"browser_specific_settings": {
"gecko": {
"id": "sourcegraph-for-firefox@sourcegraph.com"
}
},
"dev": {
"permissions": [
"storage",
"activeTab",
"contextMenus",
"https://github.com/*",
"https://gitlab.com/*",
"https://localhost:3443/*",
"https://sourcegraph.com/*",
"http://localhost:32773/*"
],
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCl2X/axNHMbP0K/NCpMzGo/pgBSsHB2xKx6tfohORKtEv2wUMBPmkK3++kirrwYO2f8Ficyq6pjlXV8LjwPSjSw9KZj6bkDn8QNoSdCp6x9i8ZOWPw6UTQ6s54b3rGQNyvrvfD7S6LphxGEx8rNlkjpWKcrvY3+DyoFKHP/hax7wIDAQAB"
},
"prod": {
"permissions": ["activeTab", "storage", "contextMenus", "https://github.com/*", "https://sourcegraph.com/*"]
}
}

View File

@ -18,7 +18,6 @@ import {
catchError,
distinctUntilChanged,
} from 'rxjs/operators'
import addDomainPermissionToggle from 'webext-domain-permission-toggle'
import { isDefined, fetchCache } from '@sourcegraph/common'
import { type GraphQLResult, requestGraphQLCommon } from '@sourcegraph/http-client'
@ -293,9 +292,6 @@ async function main(): Promise<void> {
// loaded in the popup or in th standalone options page.
browser.browserAction.setPopup({ popup: 'options.html?popup=true' })
// Add "Enable Sourcegraph on this domain" context menu item
addDomainPermissionToggle()
const ENDPOINT_KIND_REGEX = /^(proxy|expose)-/
const portKind = (port: browser.runtime.Port): string | undefined => {

View File

@ -162,7 +162,10 @@ declare namespace browser.browserAction {
function openPopup(): Promise<void>
function setBadgeText(details: { text: string | null; tabId?: number }): void
function getBadgeText(details: { tabId?: number }): Promise<string>
function setBadgeBackgroundColor(details: { color: string | ColorArray | null; tabId?: number }): void
function setBadgeBackgroundColor(details: {
color: string | ColorArray | null
tabId?: number
}): void
function getBadgeBackgroundColor(details: { tabId?: number }): Promise<ColorArray>
interface SetBadgeTextColorDetails {
@ -176,12 +179,10 @@ declare namespace browser.browserAction {
}
function setBadgeTextColor(details: SetBadgeTextColorDetails & { tabId?: number }): void
// a union type would allow specifying both, which is not allowed.
// eslint-disable-next-line @typescript-eslint/unified-signatures
function setBadgeTextColor(details: SetBadgeTextColorDetails & { windowId?: number }): void
function getBadgeTextColor(details: { tabId?: string }): Promise<ColorArray>
// a union type would allow specifying both, which is not allowed.
// eslint-disable-next-line @typescript-eslint/unified-signatures
function getBadgeTextColor(details: { windowId?: string }): Promise<ColorArray>
function enable(tabId?: number): void
@ -342,7 +343,11 @@ declare namespace browser.contextualIdentities {
name: string
}
function create(details: { name: string; color: IdentityColor; icon: IdentityIcon }): Promise<ContextualIdentity>
function create(details: {
name: string
color: IdentityColor
icon: IdentityIcon
}): Promise<ContextualIdentity>
function get(cookieStoreId: string): Promise<ContextualIdentity | null>
function query(details: { name?: string }): Promise<ContextualIdentity[]>
function update(
@ -424,13 +429,19 @@ declare namespace browser.contentScripts {
unregister: () => void
}
function register(contentScriptOptions: RegisteredContentScriptOptions): Promise<RegisteredContentScript>
function register(
contentScriptOptions: RegisteredContentScriptOptions
): Promise<RegisteredContentScript>
}
declare namespace browser.devtools.inspectedWindow {
const tabId: number
function reload(reloadOptions?: { ignoreCache?: boolean; userAgent?: string; injectedScript?: string }): void
function reload(reloadOptions?: {
ignoreCache?: boolean
userAgent?: string
injectedScript?: string
}): void
}
declare namespace browser.devtools.network {
@ -690,7 +701,10 @@ declare namespace browser.history {
function deleteUrl(details: { url: string }): Promise<void>
function deleteRange(range: { startTime: number | string | Date; endTime: number | string | Date }): Promise<void>
function deleteRange(range: {
startTime: number | string | Date
endTime: number | string | Date
}): Promise<void>
function deleteAll(): Promise<void>
@ -787,8 +801,12 @@ declare namespace browser.omnibox {
function setDefaultSuggestion(suggestion: { description: string }): void
const onInputStarted: EventEmitter<void>
const onInputChanged: CallbackEventEmitter<(text: string, suggest: (arg: SuggestResult[]) => void) => void>
const onInputEntered: CallbackEventEmitter<(text: string, disposition: OnInputEnteredDisposition) => void>
const onInputChanged: CallbackEventEmitter<
(text: string, suggest: (arg: SuggestResult[]) => void) => void
>
const onInputEntered: CallbackEventEmitter<
(text: string, disposition: OnInputEnteredDisposition) => void
>
const onInputCancelled: EventEmitter<void>
}
@ -803,7 +821,11 @@ declare namespace browser.pageAction {
function getTitle(details: { tabId: number }): Promise<string>
function setIcon(details: { tabId: number; path?: string | object; imageData?: ImageDataType }): Promise<void>
function setIcon(details: {
tabId: number
path?: string | object
imageData?: ImageDataType
}): Promise<void>
function setPopup(details: { tabId: number; popup: string }): void
@ -1403,7 +1425,10 @@ declare namespace browser.tabs {
function captureVisibleTab(windowId?: number, options?: extensionTypes.ImageDetails): Promise<string>
function detectLanguage(tabId?: number): Promise<string>
function duplicate(tabId: number): Promise<Tab>
function executeScript(tabId: number | undefined, details: extensionTypes.InjectDetails): Promise<object[]>
function executeScript(
tabId: number | undefined,
details: extensionTypes.InjectDetails
): Promise<object[]>
function get(tabId: number): Promise<Tab>
// deprecated: function getAllInWindow(): x;
function getCurrent(): Promise<Tab>
@ -1415,7 +1440,10 @@ declare namespace browser.tabs {
// windowId?: number,
// tabs: number[]|number,
// }): Promise<browser.windows.Window>;
function insertCSS(tabId: number | undefined, details: extensionTypes.InjectDetailsCSS): Promise<void>
function insertCSS(
tabId: number | undefined,
details: extensionTypes.InjectDetailsCSS
): Promise<void>
function removeCSS(tabId: number | undefined, details: extensionTypes.InjectDetails): Promise<void>
function move(
tabIds: number | number[],
@ -1930,7 +1958,10 @@ declare namespace browser.windows {
function getCurrent(getInfo?: { populate?: boolean; windowTypes?: WindowType[] }): Promise<Window>
function getLastFocused(getInfo?: { populate?: boolean; windowTypes?: WindowType[] }): Promise<Window>
function getLastFocused(getInfo?: {
populate?: boolean
windowTypes?: WindowType[]
}): Promise<Window>
function getAll(getInfo?: { populate?: boolean; windowTypes?: WindowType[] }): Promise<Window[]>

View File

@ -427,7 +427,6 @@
"utility-types": "^3.10.0",
"uuid": "^8.3.0",
"vscode-uri": "^3.0.7",
"webext-domain-permission-toggle": "^1.0.1",
"webextension-polyfill": "^0.6.0",
"whatwg-url": "^14.0.0",
"yaml-ast-parser": "^0.0.43",

View File

@ -487,9 +487,6 @@ importers:
vscode-uri:
specifier: ^3.0.7
version: 3.0.7
webext-domain-permission-toggle:
specifier: ^1.0.1
version: 1.0.1
webextension-polyfill:
specifier: ^0.6.0
version: 0.6.0
@ -6037,7 +6034,7 @@ packages:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 20.11.19
'@types/node': 20.8.0
jest-mock: 29.7.0
dev: false
@ -6047,7 +6044,7 @@ packages:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
'@types/node': 20.11.19
'@types/node': 20.8.0
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@ -6086,7 +6083,7 @@ packages:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 20.11.19
'@types/node': 20.8.0
'@types/yargs': 15.0.19
chalk: 4.1.2
dev: false
@ -6109,7 +6106,7 @@ packages:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 20.11.19
'@types/node': 20.8.0
'@types/yargs': 17.0.23
chalk: 4.1.2
@ -10337,7 +10334,7 @@ packages:
'@storybook/preview-api': 7.6.17
'@storybook/theming': 7.6.17(react-dom@18.1.0)(react@18.1.0)
'@storybook/types': 7.6.17
'@types/lodash': 4.17.0
'@types/lodash': 4.14.167
color-convert: 2.0.1
dequal: 2.0.3
lodash: 4.17.21
@ -11976,13 +11973,6 @@ packages:
'@types/responselike': 1.0.0
dev: false
/@types/chrome@0.0.106:
resolution: {integrity: sha512-sCQa+QJL/Kl+6ngU4xD1VjTFIkQDizOHN94zbb7byK9D3U79x/KXjqXyS7lHoa2q9xw3bWePb+giLhBKOILRuA==}
dependencies:
'@types/filesystem': 0.0.29
'@types/har-format': 1.2.4
dev: false
/@types/chrome@0.0.127:
resolution: {integrity: sha512-hBB9EApLYKKn2GvklVkTxVP6vZvxsH9okyIRUinNtMzZHIgIKWQk/ESbX+O5g4Bihfy38+aFGn7Kl7Cxou5JUg==}
dependencies:
@ -12184,9 +12174,11 @@ packages:
resolution: {integrity: sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw==}
dependencies:
'@types/filewriter': 0.0.28
dev: true
/@types/filewriter@0.0.28:
resolution: {integrity: sha512-AR2KUJIMdSfl/SaAHpRotBAlaJpmhgHwehEeSJQOG0hS3IrjDU16xUEEUTdqcvdLa1q16ZK5MMrtOagfLvm0gw==}
dev: true
/@types/find-cache-dir@3.2.1:
resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==}
@ -12214,11 +12206,12 @@ packages:
/@types/graceful-fs@4.1.5:
resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==}
dependencies:
'@types/node': 20.11.19
'@types/node': 20.8.0
dev: true
/@types/har-format@1.2.4:
resolution: {integrity: sha512-iUxzm1meBm3stxUMzRqgOVHjj4Kgpgu5w9fm4X7kPRfSgVRzythsucEN7/jtOo8SQzm+HfcxWWzJS0mJDH/3DQ==}
dev: true
/@types/hast@2.3.1:
resolution: {integrity: sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q==}
@ -15002,7 +14995,7 @@ packages:
engines: {node: '>=12.13.0'}
hasBin: true
dependencies:
'@types/node': 20.11.19
'@types/node': 20.8.0
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@ -15037,7 +15030,7 @@ packages:
/chromium-edge-launcher@1.0.0:
resolution: {integrity: sha512-pgtgjNKZ7i5U++1g1PWv75umkHvhVTDOQIZ+sjeUX9483S7Y6MUvO0lrd7ShGlQlFHMN4SwKTCq/X8hWrbv2KA==}
dependencies:
'@types/node': 20.11.19
'@types/node': 20.8.0
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@ -20183,7 +20176,7 @@ packages:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 20.11.19
'@types/node': 20.8.0
jest-mock: 29.7.0
jest-util: 29.7.0
dev: false
@ -20204,7 +20197,7 @@ packages:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.5
'@types/node': 20.11.19
'@types/node': 20.8.0
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@ -20262,7 +20255,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.6.3
'@types/node': 20.11.19
'@types/node': 20.8.0
jest-util: 29.7.0
dev: false
@ -20276,7 +20269,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.6.3
'@types/node': 20.11.19
'@types/node': 20.8.0
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@ -20307,7 +20300,7 @@ packages:
resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@types/node': 20.11.19
'@types/node': 20.8.0
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
@ -28239,18 +28232,6 @@ packages:
tslib: 2.1.0
dev: true
/webext-additional-permissions@1.0.0:
resolution: {integrity: sha512-QHMAah++muVoK/d+aYM56BY2DdpMAFMvW/dG28/+SdYJxgUsShsm3fOuwCPIiAIgsC8lsM52hZNJ1ROeii1lKg==}
dependencies:
'@types/chrome': 0.0.106
dev: false
/webext-domain-permission-toggle@1.0.1:
resolution: {integrity: sha512-vP3Io5KB+9YSlexDpSykcPKyu/U9pH07YGRIN1snwl92mvHy0aQlOmwYhAdfxLf4KKT7E1te1Bo0+ZvZJYFK2g==}
dependencies:
webext-additional-permissions: 1.0.0
dev: false
/webextension-polyfill@0.6.0:
resolution: {integrity: sha512-PlYwiX8e4bNZrEeBFxbFFsLtm0SMPxJliLTGdNCA0Bq2XkWrAn2ejUd+89vZm+8BnfFB1BclJyCz3iKsm2atNg==}
dev: false

View File

@ -2615,8 +2615,6 @@ PNPM,web-encoding,1.1.5,MIT,"",Approved
PNPM,web-streams-polyfill,3.2.1,MIT,"",Approved
PNPM,webcomponents.js,0.7.20,New BSD,"",Approved
PNPM,webcrypto-core,1.7.5,MIT,"",Approved
PNPM,webext-additional-permissions,1.0.0,MIT,"",Approved
PNPM,webext-domain-permission-toggle,1.0.1,MIT,"",Approved
PNPM,webextension-polyfill,0.6.0,Mozilla Public License 2.0,"",Approved
PNPM,webidl-conversions,7.0.0,Simplified BSD,"",Approved
PNPM,webpack,5.88.2,MIT,"",Approved

1 package_manager name version licenses homepage approved
2615 PNPM web-streams-polyfill 3.2.1 MIT Approved
2616 PNPM webcomponents.js 0.7.20 New BSD Approved
2617 PNPM webcrypto-core 1.7.5 MIT Approved
PNPM webext-additional-permissions 1.0.0 MIT Approved
PNPM webext-domain-permission-toggle 1.0.1 MIT Approved
2618 PNPM webextension-polyfill 0.6.0 Mozilla Public License 2.0 Approved
2619 PNPM webidl-conversions 7.0.0 Simplified BSD Approved
2620 PNPM webpack 5.88.2 MIT Approved