You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
3.7 KiB
142 lines
3.7 KiB
'use strict'
|
|
/* eslint-env browser */
|
|
/* globals chrome */
|
|
|
|
const Content = {
|
|
/**
|
|
* Initialise content script
|
|
*/
|
|
async init() {
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
|
|
try {
|
|
// HTML
|
|
let html = new XMLSerializer().serializeToString(document)
|
|
|
|
// Discard the middle portion of HTML to avoid performance degradation on large pages
|
|
const chunks = []
|
|
const maxCols = 2000
|
|
const maxRows = 3000
|
|
const rows = html.length / maxCols
|
|
|
|
for (let i = 0; i < rows; i += 1) {
|
|
if (i < maxRows / 2 || i > rows - maxRows / 2) {
|
|
chunks.push(html.slice(i * maxCols, (i + 1) * maxCols))
|
|
}
|
|
}
|
|
|
|
html = chunks.join('\n')
|
|
|
|
// Determine language based on the HTML lang attribute or content
|
|
const language =
|
|
document.documentElement.getAttribute('lang') ||
|
|
document.documentElement.getAttribute('xml:lang') ||
|
|
(await new Promise((resolve) =>
|
|
chrome.i18n.detectLanguage
|
|
? chrome.i18n.detectLanguage(html, ({ languages }) =>
|
|
resolve(
|
|
languages
|
|
.filter(({ percentage }) => percentage >= 75)
|
|
.map(({ language: lang }) => lang)[0]
|
|
)
|
|
)
|
|
: resolve()
|
|
))
|
|
|
|
// Script tags
|
|
const scripts = Array.from(document.scripts)
|
|
.filter(({ src }) => src)
|
|
.map(({ src }) => src)
|
|
.filter((script) => script.indexOf('data:text/javascript;') !== 0)
|
|
|
|
// Meta tags
|
|
const meta = Array.from(document.querySelectorAll('meta')).reduce(
|
|
(metas, meta) => {
|
|
const key = meta.getAttribute('name') || meta.getAttribute('property')
|
|
|
|
if (key) {
|
|
metas[key.toLowerCase()] = [meta.getAttribute('content')]
|
|
}
|
|
|
|
return metas
|
|
},
|
|
{}
|
|
)
|
|
|
|
Content.driver('onContentLoad', [
|
|
location.href,
|
|
{ html, scripts, meta },
|
|
language
|
|
])
|
|
|
|
Content.onGetTechnologies(await Content.driver('getTechnologies'))
|
|
} catch (error) {
|
|
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
|
|
* @param {Array} technologies
|
|
*/
|
|
onGetTechnologies(technologies = []) {
|
|
// Inject a script tag into the page to access methods of the window object
|
|
const script = document.createElement('script')
|
|
|
|
script.onload = () => {
|
|
const onMessage = ({ data }) => {
|
|
if (!data.wappalyzer || !data.wappalyzer.js) {
|
|
return
|
|
}
|
|
|
|
window.removeEventListener('message', onMessage)
|
|
|
|
chrome.runtime.sendMessage({
|
|
source: 'content.js',
|
|
func: 'analyzeJs',
|
|
args: [location.href, data.wappalyzer.js]
|
|
})
|
|
|
|
script.remove()
|
|
}
|
|
|
|
window.addEventListener('message', onMessage)
|
|
|
|
window.postMessage({
|
|
wappalyzer: {
|
|
technologies: technologies
|
|
.filter(({ js }) => Object.keys(js).length)
|
|
.map(({ name, js }) => ({ name, chains: Object.keys(js) }))
|
|
}
|
|
})
|
|
}
|
|
|
|
script.setAttribute('src', chrome.extension.getURL('js/inject.js'))
|
|
|
|
document.body.appendChild(script)
|
|
}
|
|
}
|
|
|
|
if (/complete|interactive|loaded/.test(document.readyState)) {
|
|
Content.init()
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', Content.init)
|
|
}
|