Use runtime.sendMessage instead of port.postMessage for Safari compatibility

main
Elbert Alias 5 years ago
parent eceaac4fd6
commit 0302eba2f4

@ -58,6 +58,7 @@ try {
} }
}) })
// Validate regular expression
try { try {
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new RegExp(regex) new RegExp(regex)
@ -65,6 +66,12 @@ try {
throw new Error(`${error.message} (${id})`) throw new Error(`${error.message} (${id})`)
} }
//
if (/\.[+*]/.test(regex)) {
console.log(regex)
}
// Count capture groups
const groups = new RegExp(`${regex}|`).exec('').length - 1 const groups = new RegExp(`${regex}|`).exec('').length - 1
if (groups > maxGroups) { if (groups > maxGroups) {

@ -20,6 +20,7 @@
"validate": "yarn run lint && jsonlint -qV ./schema.json ./src/apps.json && node ./bin/validate.js", "validate": "yarn run lint && jsonlint -qV ./schema.json ./src/apps.json && node ./bin/validate.js",
"convert": "cd ./src/drivers/webextension/images/icons ; cp *.svg converted ; cd converted ; convert-svg-to-png *.svg --width 32 --height 32 ; rm *.svg", "convert": "cd ./src/drivers/webextension/images/icons ; cp *.svg converted ; cd converted ; convert-svg-to-png *.svg --width 32 --height 32 ; rm *.svg",
"prettify": "jsonlint -si --trim-trailing-commas --enforce-double-quotes ./src/apps.json", "prettify": "jsonlint -si --trim-trailing-commas --enforce-double-quotes ./src/apps.json",
"build": "yarn run link && yarn run validate && yarn run prettify && yarn run convert && node ./bin/build.js" "build": "yarn run link && yarn run validate && yarn run prettify && yarn run convert && node ./bin/build.js",
"build:safari": "xcrun safari-web-extension-converter --swift --project-location build --bundle-identifier com.wappalyzer.webextension --force src/drivers/webextension"
} }
} }

@ -9,17 +9,6 @@ const Content = {
async init() { async init() {
await new Promise((resolve) => setTimeout(resolve, 1000)) await new Promise((resolve) => setTimeout(resolve, 1000))
// Enable messaging to and from driver.js
Content.port = chrome.runtime.connect({ name: 'content.js' })
Content.port.onMessage.addListener(({ func, args }) => {
const onFunc = `on${func.charAt(0).toUpperCase() + func.slice(1)}`
if (Content[onFunc]) {
Content[onFunc](args)
}
})
try { try {
// HTML // HTML
let html = new XMLSerializer().serializeToString(document) let html = new XMLSerializer().serializeToString(document)
@ -43,13 +32,15 @@ const Content = {
document.documentElement.getAttribute('lang') || document.documentElement.getAttribute('lang') ||
document.documentElement.getAttribute('xml:lang') || document.documentElement.getAttribute('xml:lang') ||
(await new Promise((resolve) => (await new Promise((resolve) =>
chrome.i18n.detectLanguage(html, ({ languages }) => chrome.i18n.detectLanguage
resolve( ? chrome.i18n.detectLanguage(html, ({ languages }) =>
languages resolve(
.filter(({ percentage }) => percentage >= 75) languages
.map(({ language: lang }) => lang)[0] .filter(({ percentage }) => percentage >= 75)
) .map(({ language: lang }) => lang)[0]
) )
)
: resolve()
)) ))
// Script tags // Script tags
@ -72,22 +63,41 @@ const Content = {
{} {}
) )
Content.port.postMessage({ Content.driver('onContentLoad', [
func: 'onContentLoad', location.href,
args: [location.href, { html, scripts, meta }, language] { html, scripts, meta },
}) language
])
Content.port.postMessage({ func: 'getTechnologies' }) Content.onGetTechnologies(await Content.driver('getTechnologies'))
} catch (error) { } catch (error) {
Content.port.postMessage({ func: 'error', args: [error, 'content.js'] }) Content.driver('error', error)
} }
}, },
driver(func, args, callback) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(
{
source: 'content.js',
func,
args: args ? (Array.isArray(args) ? args : [args]) : []
},
(response) => {
chrome.runtime.lastError
? reject(new Error(chrome.runtime.lastError.message))
: resolve(response)
}
)
})
},
/** /**
* Callback for getTechnologies * Callback for getTechnologies
* @param {Object} technologies * @param {Array} technologies
*/ */
onGetTechnologies(technologies) { onGetTechnologies(technologies = []) {
console.log({ technologies })
// Inject a script tag into the page to access methods of the window object // Inject a script tag into the page to access methods of the window object
const script = document.createElement('script') const script = document.createElement('script')
@ -99,7 +109,8 @@ const Content = {
window.removeEventListener('message', onMessage) window.removeEventListener('message', onMessage)
Content.port.postMessage({ chrome.runtime.sendMessage({
source: 'content.js',
func: 'analyzeJs', func: 'analyzeJs',
args: [location.href, data.wappalyzer.js] args: [location.href, data.wappalyzer.js]
}) })

@ -63,7 +63,7 @@ const Driver = {
chrome.tabs.onRemoved.addListener((id) => (Driver.cache.tabs[id] = null)) chrome.tabs.onRemoved.addListener((id) => (Driver.cache.tabs[id] = null))
// Enable messaging between scripts // Enable messaging between scripts
chrome.runtime.onConnect.addListener(Driver.onRuntimeConnect) chrome.runtime.onMessage.addListener(Driver.onMessage)
const { version } = chrome.runtime.getManifest() const { version } = chrome.runtime.getManifest()
const previous = await getOption('version') const previous = await getOption('version')
@ -153,29 +153,28 @@ const Driver = {
/** /**
* Enable scripts to call Driver functions through messaging * Enable scripts to call Driver functions through messaging
* @param {Object} port * @param {Object} message
* @param {Object} sender
* @param {Function} callback
*/ */
onRuntimeConnect(port) { onMessage({ source, func, args }, sender, callback) {
Driver.log(`Connected to ${port.name}`) if (!func) {
return
}
port.onMessage.addListener(async ({ func, args }) => { Driver.log({ source, func, args })
if (!func) {
return
}
Driver.log({ port: port.name, func, args }) if (!Driver[func]) {
Driver.error(new Error(`Method does not exist: Driver.${func}`))
if (!Driver[func]) { return
Driver.error(new Error(`Method does not exist: Driver.${func}`)) }
return Promise.resolve(Driver[func].call(Driver[func], ...(args || [])))
} .then(callback)
.catch(Driver.error)
port.postMessage({ return !!callback
func,
args: await Driver[func].call(port.sender, ...(args || []))
})
})
}, },
/** /**
@ -362,6 +361,10 @@ const Driver = {
* @param {Object} technologies * @param {Object} technologies
*/ */
async setIcon(url, technologies) { async setIcon(url, technologies) {
if (!chrome.pageAction.show) {
return
}
const dynamicIcon = await getOption('dynamicIcon', true) const dynamicIcon = await getOption('dynamicIcon', true)
let icon = 'default.svg' let icon = 'default.svg'
@ -380,16 +383,19 @@ const Driver = {
await Promise.all( await Promise.all(
tabs.map(async ({ id: tabId }) => { tabs.map(async ({ id: tabId }) => {
await promisify(chrome.pageAction, 'setIcon', { await Promise.race([
tabId, promisify(chrome.pageAction, 'setIcon', {
path: chrome.extension.getURL( tabId,
`../images/icons/${ path: chrome.extension.getURL(
/\.svg$/i.test(icon) `../images/icons/${
? `converted/${icon.replace(/\.svg$/, '.png')}` /\.svg$/i.test(icon)
: icon ? `converted/${icon.replace(/\.svg$/, '.png')}`
}` : icon
) }`
}) )
}),
new Promise((resolve) => setTimeout(resolve, 500))
])
chrome.pageAction.show(tabId) chrome.pageAction.show(tabId)
}) })
@ -519,7 +525,7 @@ const Driver = {
] ]
if ( if (
!/((local|dev(elopment)?|stag(e|ing)?|test(ing)?|demo(shop)?|admin|google|cache)\.|\/admin|\.local)/.test( !/((local|dev(elopment)?|stag(e|ing)?|test(ing)?|demo(shop)?|admin|google|cache)\.|\/admin|\.local|127\.)/.test(
hostname hostname
) && ) &&
hits >= 3 hits >= 3

@ -2,11 +2,17 @@
/* eslint-env browser */ /* eslint-env browser */
/* globals chrome, Utils */ /* globals chrome, Utils */
const { agent, open, i18n, getOption, setOption, promisify } = Utils const {
agent,
open,
i18n,
getOption,
setOption,
promisify,
sendMessage
} = Utils
const Popup = { const Popup = {
port: chrome.runtime.connect({ name: 'popup.js' }),
/** /**
* Initialise popup * Initialise popup
*/ */
@ -34,7 +40,7 @@ const Popup = {
agent === 'chrome' || (await getOption('termsAccepted', false)) agent === 'chrome' || (await getOption('termsAccepted', false))
if (termsAccepted) { if (termsAccepted) {
Popup.driver('getDetections') Popup.onGetDetections(await Popup.driver('getDetections'))
} else { } else {
document.querySelector('.terms').style.display = 'flex' document.querySelector('.terms').style.display = 'flex'
document.querySelector('.detections').style.display = 'none' document.querySelector('.detections').style.display = 'none'
@ -47,7 +53,7 @@ const Popup = {
document.querySelector('.detections').style.display = 'block' document.querySelector('.detections').style.display = 'block'
document.querySelector('.empty').style.display = 'block' document.querySelector('.empty').style.display = 'block'
Popup.driver('getDetections') chrome.runtime.sendMessage('getDetections', Popup.onGetDetections)
}) })
// Apply internationalization // Apply internationalization
@ -71,13 +77,8 @@ const Popup = {
.addEventListener('click', () => chrome.runtime.openOptionsPage()) .addEventListener('click', () => chrome.runtime.openOptionsPage())
}, },
/** driver(func, args) {
* Call a function on driver.js through messaging return sendMessage('popup.js', func, args)
* @param {function} func
* @param {...any} args
*/
driver(func, ...args) {
Popup.port.postMessage({ func, args })
}, },
/** /**
@ -85,7 +86,7 @@ const Popup = {
* @param {String} message * @param {String} message
*/ */
log(message) { log(message) {
Popup.driver('log', message, 'popup.js') Popup.driver('log', message)
}, },
/** /**
@ -113,7 +114,7 @@ const Popup = {
* Callback for getDetection listener * Callback for getDetection listener
* @param {Array} detections * @param {Array} detections
*/ */
async onGetDetections(detections) { async onGetDetections(detections = []) {
const pinnedCategory = await getOption('pinnedCategory') const pinnedCategory = await getOption('pinnedCategory')
if (detections.length) { if (detections.length) {
@ -216,15 +217,6 @@ const Popup = {
} }
} }
// Add listener for popup PostMessage API
Popup.port.onMessage.addListener(({ func, args }) => {
const onFunc = `on${func.charAt(0).toUpperCase() + func.slice(1)}`
if (Popup[onFunc]) {
Popup[onFunc](args)
}
})
if (/complete|interactive|loaded/.test(document.readyState)) { if (/complete|interactive|loaded/.test(document.readyState)) {
Popup.init() Popup.init()
} else { } else {

@ -75,5 +75,22 @@ const Utils = {
Array.from(document.querySelectorAll('[data-i18n]')).forEach( Array.from(document.querySelectorAll('[data-i18n]')).forEach(
(node) => (node.innerHTML = chrome.i18n.getMessage(node.dataset.i18n)) (node) => (node.innerHTML = chrome.i18n.getMessage(node.dataset.i18n))
) )
},
sendMessage(source, func, args) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(
{
source,
func,
args: args ? (Array.isArray(args) ? args : [args]) : []
},
(response) => {
chrome.runtime.lastError
? reject(new Error(chrome.runtime.lastError.message))
: resolve(response)
}
)
})
} }
} }