Skip to content

Commit 8ea5079

Browse files
authored
feat: Improve script injection and migrate to Manifest V3 (#18)
1 parent 4cd9449 commit 8ea5079

File tree

10 files changed

+170
-32
lines changed

10 files changed

+170
-32
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
/*.log
33
/dist
44
/dist-zip
5+
/dist-firefox
6+
/dist-zip-firefox
57
coverage
68
.env
79
.DS_Store

src/manifest.json renamed to chrome/manifest.json

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"name": "Vue Telescope",
33
"description": "Discover Vue.js Websites",
44
"version": "1.0.0",
5-
"manifest_version": 2,
5+
"manifest_version": 3,
6+
"minimum_chrome_version": "102",
67
"icons": {
78
"16": "icons/icon-16.png",
89
"48": "icons/icon-48.png",
@@ -15,7 +16,7 @@
1516
"js": ["content.js"]
1617
}
1718
],
18-
"browser_action": {
19+
"action": {
1920
"default_icon": {
2021
"16": "icons/icon-grey-16.png",
2122
"48": "icons/icon-grey-48.png",
@@ -25,9 +26,24 @@
2526
"default_popup": "popup/popup.html"
2627
},
2728
"background": {
28-
"scripts": ["background.js"]
29+
"service_worker": "background.js"
2930
},
30-
"permissions": ["https://*/*"],
31-
"content_security_policy": "script-src 'self'; object-src 'self'",
32-
"web_accessible_resources": ["injected.js"]
31+
"permissions": ["https://*/*", "scripting"],
32+
"host_permissions": [
33+
"<all_urls>"
34+
],
35+
"content_security_policy": {
36+
"extension_pages": "script-src 'self'; object-src 'self'"
37+
},
38+
"web_accessible_resources": [
39+
{
40+
"resources": [
41+
"injected.js"
42+
],
43+
"matches": [
44+
"<all_urls>"
45+
],
46+
"extension_ids": []
47+
}
48+
]
3349
}

firefox/manifest.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "Vue Telescope",
3+
"description": "Discover Vue.js Websites",
4+
"version": "1.0.0",
5+
"manifest_version": 2,
6+
"icons": {
7+
"16": "icons/icon-16.png",
8+
"48": "icons/icon-48.png",
9+
"128": "icons/icon-128.png"
10+
},
11+
"content_scripts": [
12+
{
13+
"run_at": "document_start",
14+
"matches": ["<all_urls>"],
15+
"js": ["content.js"]
16+
}
17+
],
18+
"browser_action": {
19+
"default_icon": {
20+
"16": "icons/icon-grey-16.png",
21+
"48": "icons/icon-grey-48.png",
22+
"128": "icons/icon-grey-128.png"
23+
},
24+
"default_title": "Vue Telescope",
25+
"default_popup": "popup/popup.html"
26+
},
27+
"background": {
28+
"scripts": ["background.js"]
29+
},
30+
"permissions": ["https://*/*"],
31+
"content_security_policy": "script-src 'self'; object-src 'self'",
32+
"web_accessible_resources": ["injected.js"]
33+
}
34+

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
"lint": "eslint --ext .js,.vue src",
1212
"build:dev": "cross-env NODE_ENV=development webpack --hide-modules",
1313
"build-zip": "node scripts/build-zip.js",
14+
"build-zip:firefox": "node scripts/build-zip.js --build-browser firefox",
1415
"watch:tailwind": "NODE_ENV=development postcss src/tailwind.css -o dist/tailwind.css -w",
1516
"dev:tailwind": "NODE_ENV=development postcss src/tailwind.css -o dist/tailwind.css",
1617
"build:tailwind": "NODE_ENV=production postcss src/tailwind.css -o dist/tailwind.css",
1718
"watch": "npm run build -- --watch",
1819
"watch:dev": "cross-env HMR=true npm run build:dev -- --watch",
1920
"dev": "concurrently \"npm run watch:tailwind\" \"npm run watch:dev\"",
2021
"build": "npm run build:tailwind && webpack --mode production",
22+
"build:firefox": "npm run build:tailwind && webpack --mode production --build-browser firefox && cp dist/tailwind.css dist-firefox/tailwind.css",
2123
"release": "standard-version && git push --follow-tags origin master"
2224
},
2325
"dependencies": {
@@ -60,13 +62,14 @@
6062
"google-fonts-webpack-plugin": "^0.4.4",
6163
"html-loader": "^1.1.0",
6264
"mini-css-extract-plugin": "^0.9.0",
65+
"minimist": "^1.2.8",
6366
"standard-version": "^8.0.2",
6467
"vue-loader": "^15.4.2",
6568
"vue-svg-loader": "^0.16.0",
6669
"vue-template-compiler": "^2.6.10",
6770
"web-ext-types": "^2.1.0",
6871
"webpack": "^4.20.2",
6972
"webpack-cli": "^3.3.10",
70-
"webpack-extension-reloader": "^1.1.0"
73+
"webpack-extension-reloader": "^1.1.4"
7174
}
7275
}

scripts/build-zip.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@ const fs = require('fs')
44
const path = require('path')
55
const archiver = require('archiver')
66

7-
const DEST_DIR = path.join(__dirname, '../dist')
8-
const DEST_ZIP_DIR = path.join(__dirname, '../dist-zip')
7+
const argv = require('minimist')(process.argv.slice(2))
8+
const browser = argv['build-browser'] || 'chrome'
9+
const isFireFox = browser === 'firefox'
10+
11+
const distSuffix = isFireFox ? '-firefox' : ''
12+
13+
const DEST_DIR = path.join(__dirname, `../dist${distSuffix}`)
14+
const DEST_ZIP_DIR = path.join(__dirname, `../dist-zip${distSuffix}`)
915

1016
const extractExtensionData = () => {
1117
const extPackageJson = require('../package.json')

src/background.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,53 @@
1+
import { IS_FIREFOX, isSupportExecutionVersion } from './utils'
12
const browser = require('webextension-polyfill')
23

34
const tabsStorage = {}
45

6+
if (!IS_FIREFOX && isSupportExecutionVersion) {
7+
/**
8+
* equivalent logic for Firefox is in content.js
9+
* Manifest V3 method of injecting content scripts (not yet supported in Firefox)
10+
Note: the "world" option in registerContentScripts is only available in Chrome v102+
11+
MAIN The execution environment of the web page. This environment is shared with the web page, without isolation.
12+
*/
13+
browser.scripting.registerContentScripts(
14+
[
15+
{
16+
id: 'injected',
17+
matches: ['<all_urls>'],
18+
js: ['injected.js'],
19+
runAt: 'document_start',
20+
world: browser.scripting.ExecutionWorld.MAIN
21+
}
22+
],
23+
function () {
24+
// When the content scripts are already registered, an error will be thrown.
25+
// It happens when the service worker process is incorrectly duplicated.
26+
if (browser.runtime.lastError) {
27+
console.error(browser.runtime.lastError)
28+
}
29+
}
30+
)
31+
}
32+
533
browser.tabs.onActivated.addListener(handleActivated)
634
browser.tabs.onUpdated.addListener(handleUpdated)
735

36+
function setIcon (details) {
37+
// because manifest version is different
38+
if (IS_FIREFOX) {
39+
browser.browserAction.setIcon(details)
40+
} else {
41+
browser.action.setIcon(details)
42+
}
43+
}
44+
845
browser.runtime.onMessage.addListener(
946
async function (message, sender, sendResponse) {
1047
if (message.action === 'analyze') {
1148
// when sending message from popup.js there's no sender.tab, so need to pass tabId
1249
const tabId = (sender.tab && sender.tab.id) || message.payload.tabId
13-
browser.browserAction.setIcon({
50+
setIcon({
1451
tabId: tabId,
1552
path: message.payload.hasVue ? 'icons/icon-128.png' : 'icons/icon-grey-128.png'
1653
})
@@ -74,7 +111,7 @@ browser.runtime.onMessage.addListener(
74111

75112
// when tab clicked
76113
async function handleActivated ({ tabId, windowId }) {
77-
browser.browserAction.setIcon({
114+
setIcon({
78115
tabId,
79116
path: tabsStorage[tabId] && tabsStorage[tabId] && tabsStorage[tabId].hasVue ? 'icons/icon-128.png' : 'icons/icon-grey-128.png'
80117
})

src/content.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1+
import { IS_FIREFOX, isSupportExecutionVersion } from './utils'
12
const browser = require('webextension-polyfill')
23

3-
// injecting the script
4+
// injecting the script, inspire by react-devtools-extensions
5+
function injectScriptSync (src) {
6+
let code = ''
7+
const request = new XMLHttpRequest()
8+
request.addEventListener('load', function () {
9+
code = this.responseText
10+
})
11+
request.open('GET', src, false)
12+
request.send()
13+
const script = document.createElement('script')
14+
script.textContent = code
15+
// This script runs before the <head> element is created, so we add the script to <html> instead.
16+
document.documentElement.appendChild(script)
17+
script.parentNode.removeChild(script)
18+
}
419

5-
const content = browser.extension.getURL('injected.js')
6-
const script = document.createElement('script')
7-
script.setAttribute('defer', 'defer')
8-
script.setAttribute('type', 'text/javascript')
9-
script.setAttribute('src', content)
10-
// document.body.appendChild(script)
11-
document.documentElement.appendChild(script)
12-
script.parentNode.removeChild(script)
20+
// equivalent logic for other browser is in background.js
21+
if (IS_FIREFOX || !isSupportExecutionVersion) {
22+
injectScriptSync(browser.extension.getURL('injected.js'))
23+
}
1324

1425
// content script logic
1526

src/utils.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0
2+
export const IS_CHROME = /Chrome/i.test(navigator.userAgent)
3+
export function getChromeVersion () {
4+
const ua = navigator.userAgent
5+
const match = ua.match(/Chrome\/(\d+)/)
6+
const version = match ? parseInt(match[1], 10) : null
7+
return version
8+
}
9+
10+
const chromeVersion = getChromeVersion()
11+
export const isSupportExecutionVersion = chromeVersion && chromeVersion >= 102

webpack.config.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ const webpack = require('webpack')
33
const ejs = require('ejs')
44
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
55
const CopyPlugin = require('copy-webpack-plugin')
6-
const ExtensionReloader = require('webpack-extension-reloader')
6+
// const ExtensionReloader = require('webpack-extension-reloader')
77
const { VueLoaderPlugin } = require('vue-loader')
88
const { version } = require('./package.json')
99
const path = require('path')
1010

11+
const argv = require('minimist')(process.argv.slice(2))
12+
const browser = argv['build-browser'] || 'chrome'
13+
const distDir = `/dist${browser === 'firefox' ? '-firefox' : ''}`
14+
1115
const config = {
1216
devtool: 'inline-source-map',
1317
mode: process.env.NODE_ENV,
@@ -19,7 +23,7 @@ const config = {
1923
content: './content.js'
2024
},
2125
output: {
22-
path: path.join(__dirname, '/dist'),
26+
path: path.join(__dirname, distDir),
2327
filename: '[name].js'
2428
// sourceMapFilename: '[name].js.map'
2529
},
@@ -88,17 +92,24 @@ const config = {
8892
{ from: 'popup/popup.html', to: 'popup/popup.html', transform: transformHtml },
8993
{
9094
from: './popup/popup.css',
91-
to: path.join(__dirname, '/dist/popup/popup.css')
95+
to: path.join(__dirname, `${distDir}/popup/popup.css`)
9296
},
9397
{
94-
from: 'manifest.json',
98+
// firefox only support manifest V2
99+
from: `../${browser}/manifest.json`,
95100
to: 'manifest.json',
96101
transform: (content) => {
97102
const jsonContent = JSON.parse(content)
98103
jsonContent.version = version
99104

100105
if (config.mode === 'development') {
101-
jsonContent.content_security_policy = "script-src 'self' 'unsafe-eval'; object-src 'self'"
106+
if (browser === 'firefox') {
107+
jsonContent.content_security_policy = "script-src 'self'; object-src 'self'"
108+
} else {
109+
jsonContent.content_security_policy = {
110+
extension_pages: "script-src 'self'; object-src 'self'"
111+
}
112+
}
102113
}
103114

104115
return JSON.stringify(jsonContent, null, 2)
@@ -130,11 +141,13 @@ if (config.mode === 'development') {
130141
}
131142

132143
if (process.env.HMR === 'true') {
133-
config.plugins = (config.plugins || []).concat([
134-
new ExtensionReloader({
135-
manifest: path.join(__dirname, '/src/manifest.json')
136-
})
137-
])
144+
// https://github.com/rubenspgcavalcante/webpack-extension-reloader/issues/125
145+
// ExtensionReloader think `background.scripts` is must, but manifest V3 can use `service_worker`
146+
// config.plugins = (config.plugins || []).concat([
147+
// new ExtensionReloader({
148+
// manifest: path.join(__dirname, '/src/manifest.json')
149+
// })
150+
// ])
138151
}
139152

140153
function transformHtml (content) {

yarn.lock

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4925,6 +4925,11 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
49254925
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
49264926
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
49274927

4928+
minimist@^1.2.8:
4929+
version "1.2.8"
4930+
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
4931+
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
4932+
49284933
mississippi@^3.0.0:
49294934
version "3.0.0"
49304935
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
@@ -7409,9 +7414,9 @@ webpack-cli@^3.3.10:
74097414
v8-compile-cache "^2.1.1"
74107415
yargs "^13.3.2"
74117416

7412-
webpack-extension-reloader@^1.1.0:
7417+
webpack-extension-reloader@^1.1.4:
74137418
version "1.1.4"
7414-
resolved "https://registry.yarnpkg.com/webpack-extension-reloader/-/webpack-extension-reloader-1.1.4.tgz#f5e5fa580e617c114cc45ddb6eb25c5d6a4dd2f6"
7419+
resolved "https://registry.npmjs.org/webpack-extension-reloader/-/webpack-extension-reloader-1.1.4.tgz#f5e5fa580e617c114cc45ddb6eb25c5d6a4dd2f6"
74157420
integrity sha512-PyssJvAiKhztc//QmhpU8yfg7LBR7Bn/cjSM7jadfQJPIDNN1Djxc+SJQRk8uHQ3GQbyWhsWu2DLCMBRcWHIPA==
74167421
dependencies:
74177422
"@types/webpack" "^4.39.8"

0 commit comments

Comments
 (0)