Skip to content

Instantly share code, notes, and snippets.

@ddarwisheh
Created May 28, 2021 11:27
Show Gist options
  • Save ddarwisheh/35576074a1450b018303d568c5f7b1a7 to your computer and use it in GitHub Desktop.
Save ddarwisheh/35576074a1450b018303d568c5f7b1a7 to your computer and use it in GitHub Desktop.
chrome extension and context
//_________________script js______________
async function test(){
return new Promise((res,rej)=>{
setTimeout(function(){
res('test async function')
},3000)
})
}
//_________________content js______________
runInPageContext({
func: async function diaa () {
let testData = await test()
return new Promise((resolve, reject) => {
resolve(testData)
})
},
call: 'diaa',
args: []
})
.then(data => {
console.log( data )
})
.catch(err => {
console.log({ err })
})
//_________________context js______________
function uuidv4 () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}
/**
* Runs a function in the page context by serializing it to a string and injecting it to the page
* @param {(function|Object)} func - a function to serialize and run in the page context, or an arguments object
* @param {function} func.func - a function to serialize and run in the page context
* @param {Array} [func.args] - arguments array to be passed to `func`
* @param {Document} [func.doc] - alternative `document` to inject the serialized function
* @param {number} [func.timeout] - optional timeout (milliseconds)
* @param {...any} [args] - arguments array to be passed to `func`
* @returns {Promise} a promise that will be resolved with the return value of the serialized function
*/
function injectElemnt (element) {
try {
;(document.head || document.body || document.documentElement).appendChild(
element
)
} catch (err) {
console.log(err)
}
}
async function runInPageContext (func, ...args) {
const params = Object(func)
const { doc = document, timeout, call } = params
if (typeof func !== 'function') {
func = params.func
args = params.args
}
// test that we are running with the allow-scripts permission
try {
window.sessionStorage
} catch (ignore) {
return null
}
let id1 = uuidv4()
let id2 = uuidv4()
// returned value container
const resultMessageId = parseInt(
'' + Math.floor(Math.random() * 100 + 1) + new Date().getTime()
)
let scriptElm2 = doc.createElement('script')
scriptElm2.id = id1
scriptElm2.setAttribute('type', 'application/javascript')
const code2 = `${func}`
scriptElm2.textContent = code2
injectElemnt(scriptElm2)
// console.log(scriptElm2)
// prepare script container
let scriptElm = doc.createElement('script')
scriptElm.setAttribute('type', 'application/javascript')
// const code = `
// (
// async function () {
// const response = {
// id: ${resultMessageId}
// };
// try {
// response.result = JSON.stringify(await (${func})(...${JSON.stringify(
// args || []
// )})); // run script
// } catch(err) {
// response.error = JSON.stringify(err);
// console.log(err)
// }
// window.postMessage(response, '*');
// }
// )();
// `
const code = `
(
async function () {
const response = {
id: ${resultMessageId}
};
try {
response.result = JSON.stringify(await window['${call}'](...${JSON.stringify(
args || []
)})); // run script
} catch(err) {
response.error = JSON.stringify(err);
console.log(err)
}
window.postMessage(response, '*');
try {
if(document.getElementById('${id1}')){
document.getElementById('${id1}').outerHTML = ''
window['${call}'] = undefined
}
} catch(err) {
console.log(err)
}
}
)();
`
// inject the script
scriptElm.id = id2
scriptElm.textContent = code
// run the script
// doc.documentElement.appendChild(scriptElm)
// console.log(scriptElm)
injectElemnt(scriptElm)
// clean up script element
scriptElm.remove()
// create a "flat" promise
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
// reject on timeout
if (timeout !== undefined) {
const timerId = setTimeout(() => {
onResult({
data: {
id: resultMessageId,
error: 'Script timeout'
}
})
}, timeout)
// clear the timeout handler
promise.finally(() => (timerId !== null ? clearTimeout(timerId) : null))
}
// resolve on result
function onResult (event) {
const data = Object(event.data)
if (data.id === resultMessageId) {
window.removeEventListener('message', onResult)
if (data.error !== undefined) {
return reject(JSON.parse(data.error))
}
return resolve(
data.result !== undefined ? JSON.parse(data.result) : undefined
)
}
}
window.addEventListener('message', onResult)
return promise
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment