Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 46 additions & 43 deletions x-frame-bypass.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,70 +9,73 @@ customElements.define('x-frame-bypass', class extends HTMLIFrameElement {
this.load(this.src)
}
connectedCallback () {
this.sandbox = '' + this.sandbox || 'allow-forms allow-modals allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation' // all except allow-top-navigation
if (!this.getAttribute('sandbox')) this.sandbox = 'allow-forms allow-modals allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts allow-top-navigation-by-user-activation'
this._onMessage = e => {
if (e.source === this.contentWindow && e.data && e.data.type === 'X-Frame-Bypass-Load') {
this.load(e.data.url, e.data.options)
}
}
window.addEventListener('message', this._onMessage)
}
disconnectedCallback () {
window.removeEventListener('message', this._onMessage)
}
load (url, options) {
if (!url) return
if (!url.startsWith('http')) throw new Error(`X-Frame-Bypass src ${url} does not start with http(s)://`)
if (!url.startsWith('http')) throw new Error('X-Frame-Bypass src ' + url + ' does not start with http(s)://')
console.log('X-Frame-Bypass loading:', url)
this.srcdoc = `<!DOCTYPE html>
<html>
<head>
<style>
.loader {
position: absolute;
top: calc(50% - 25px);
left: calc(50% - 25px);
width: 50px;
height: 50px;
background-color: #333;
border-radius: 50%;
animation: loader 1s infinite ease-in-out;
}
@keyframes loader {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
opacity: 0;
this.srcdoc = '<!DOCTYPE html><html><head><style>.loader {position: absolute;top: calc(50% - 25px);left: calc(50% - 25px);width: 50px;height: 50px;background-color: #333;border-radius: 50%;animation: loader 1s infinite ease-in-out;}@keyframes loader {0% {transform: scale(0);}100% {transform: scale(1);opacity: 0;}}</style></head><body><div class="loader"></div></body></html>'
if (options && options.body && !(options.body instanceof FormData)) {
const formData = new FormData()
Object.entries(options.body).forEach(([key, value]) => formData.append(key, value))
options.body = formData
}
}
</style>
</head>
<body>
<div class="loader"></div>
</body>
</html>`
this.fetchProxy(url, options, 0).then(res => res.text()).then(data => {
if (data) this.srcdoc = data.replace(/<head([^>]*)>/i, `<head$1>
<base href="${url}">
<script>
if (!data) return
const script = `<script>
// X-Frame-Bypass navigation event handlers
document.addEventListener('click', e => {
if (frameElement && document.activeElement && document.activeElement.href) {
const a = e.target.closest('a')
if (a && a.href) {
if (a.target && a.target !== '_self') return
e.preventDefault()
frameElement.load(document.activeElement.href)
window.parent.postMessage({type: 'X-Frame-Bypass-Load', url: a.href}, '*')
}
})
document.addEventListener('submit', e => {
if (frameElement && document.activeElement && document.activeElement.form && document.activeElement.form.action) {
e.preventDefault()
if (document.activeElement.form.method === 'post') frameElement.load(document.activeElement.form.action, {method: 'post', body: new FormData(document.activeElement.form)})
else frameElement.load(document.activeElement.form.action + '?' + new URLSearchParams(new FormData(document.activeElement.form)))
e.preventDefault()
const form = e.target
const formData = new FormData(form)
if (form.method === 'post') {
const body = {}
formData.forEach((value, key) => { body[key] = value })
window.parent.postMessage({type: 'X-Frame-Bypass-Load', url: form.action, options: {method: 'post', body}}, '*')
} else {
window.parent.postMessage({type: 'X-Frame-Bypass-Load', url: form.action + '?' + new URLSearchParams(formData)}, '*')
}
})
</script>`).replace(/ crossorigin=['"][^'"]*['"]/gi, '')
</script>`
let html = data.replace(/<head([^>]*)>/i, '<head$1><base href="' + url + '">' + script)
if (html === data) {
html = '<base href="' + url + '">' + script + data
}
this.srcdoc = html.replace(/ crossorigin=['"][^'"]*['"]/gi, '')
}).catch(e => console.error('Cannot load X-Frame-Bypass:', e))
}
fetchProxy (url, options, i) {
const proxies = (options || {}).proxies || [
const proxies = (options || {}).proxies || (this.getAttribute('proxies') ? this.getAttribute('proxies').split(',') : [
'https://api.allorigins.win/raw?url=',
'https://api.codetabs.com/v1/proxy/?quest=',
'https://cors-anywhere.herokuapp.com/'
]
])
if (url.startsWith('http://localhost') || url.startsWith('http://127.0.0.1')) {
return fetch(url, options).then(res => {
if (!res.ok) throw new Error(res.status + ' ' + res.statusText);
return res
})
}
return fetch(proxies[i] + encodeURIComponent(url), options).then(res => {
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
if (!res.ok) throw new Error(res.status + ' ' + res.statusText);
return res
}).catch(error => {
if (i === proxies.length - 1) throw error
Expand Down