151 lines
5.3 KiB
JavaScript
151 lines
5.3 KiB
JavaScript
//
|
|
// The first script to be run on every page as to capture media elements
|
|
// when they're created.
|
|
//
|
|
// Has to be run before anything else is executed in the document.
|
|
// Declaring the script in the manifest, as opposed to using
|
|
// chrome.tabs.executeScript, seems to be the only way to achieve this from
|
|
// my own testing.
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=471801
|
|
//
|
|
|
|
;(function() {
|
|
// Only run in iframes, the same as Metastream webviews
|
|
if (window.self === window.top) return
|
|
|
|
const mainWorldScript = function() {
|
|
document.getElementById('metastreaminitscript').remove()
|
|
|
|
const INIT_TIMEOUT = 5e3
|
|
const isFirefox = navigator.userAgent.toLowerCase().includes('firefox')
|
|
|
|
//=========================================================================
|
|
// document.createElement proxy
|
|
//=========================================================================
|
|
|
|
window.__metastreamMediaElements = new Set()
|
|
|
|
// Proxy document.createElement to trap media elements created in-memory
|
|
const origCreateElement = document.createElement
|
|
const proxyCreateElement = function() {
|
|
const element = origCreateElement.apply(document, arguments)
|
|
if (window.__metastreamMediaElements && element instanceof HTMLMediaElement) {
|
|
window.__metastreamMediaElements.add(element)
|
|
}
|
|
return element
|
|
}
|
|
proxyCreateElement.toString = origCreateElement.toString.bind(origCreateElement)
|
|
document.createElement = proxyCreateElement
|
|
|
|
setTimeout(() => {
|
|
if (window.__metastreamMediaElements) {
|
|
window.__metastreamMediaElements.clear()
|
|
window.__metastreamMediaElements = undefined
|
|
}
|
|
}, INIT_TIMEOUT)
|
|
|
|
//=========================================================================
|
|
// navigator.mediaSession proxy (Firefox)
|
|
//=========================================================================
|
|
|
|
if (isFirefox) {
|
|
// stub out MediaSession API until Firefox supports this natively
|
|
if (!navigator.mediaSession) {
|
|
const noop = () => {}
|
|
const mediaSessionStub = {
|
|
__installedByMetastreamRemote__: true,
|
|
setActionHandler: noop
|
|
}
|
|
Object.defineProperty(window.navigator, 'mediaSession', {
|
|
value: mediaSessionStub,
|
|
enumerable: false,
|
|
writable: true
|
|
})
|
|
|
|
function MediaMetadata(metadata) {
|
|
Object.assign(this, metadata)
|
|
}
|
|
window.MediaMetadata = MediaMetadata
|
|
}
|
|
|
|
const { mediaSession } = navigator
|
|
|
|
// Capture action handlers for player.js proxy
|
|
mediaSession._handlers = {}
|
|
|
|
const _setActionHandler = mediaSession.setActionHandler
|
|
mediaSession.setActionHandler = function(name, handler) {
|
|
mediaSession._handlers[name] = handler
|
|
_setActionHandler.apply(mediaSession, arguments)
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// document.domain fix (Firefox)
|
|
//=========================================================================
|
|
|
|
if (isFirefox) {
|
|
const domains = ['twitch.tv', 'crunchyroll.com']
|
|
|
|
// Fix for setting document.domain in sandboxed iframe
|
|
try {
|
|
const { domain } = document
|
|
if (domain && domains.some(d => domain.includes(d))) {
|
|
Object.defineProperty(document, 'domain', {
|
|
value: domain,
|
|
writable: true
|
|
})
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Inline script embed prevention fix
|
|
//=========================================================================
|
|
|
|
const observeScripts = () => {
|
|
const scriptSnippets = [
|
|
{ code: 'window.top !== window.self', replacement: 'false' },
|
|
{ code: 'self == top', replacement: 'true' },
|
|
{ code: 'top.location != window.location', replacement: 'false' }
|
|
]
|
|
|
|
const getAddedScripts = mutationList =>
|
|
mutationList.reduce((scripts, mutation) => {
|
|
if (mutation.type !== 'childList') return scripts
|
|
const inlineScripts = Array.from(mutation.addedNodes).filter(
|
|
node => node instanceof HTMLScriptElement && node.innerHTML.length > 0
|
|
)
|
|
return inlineScripts.length > 0 ? [...scripts, ...inlineScripts] : scripts
|
|
}, [])
|
|
|
|
// Modifies inline scripts to allow embedding content in iframe
|
|
const inlineScriptModifier = mutationsList => {
|
|
const scripts = getAddedScripts(mutationsList)
|
|
for (let script of scripts) {
|
|
for (let snippet of scriptSnippets) {
|
|
if (script.innerHTML.includes(snippet.code)) {
|
|
script.innerHTML = script.innerHTML.split(snippet.code).join(snippet.replacement)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const observer = new MutationObserver(inlineScriptModifier)
|
|
observer.observe(document.documentElement, { childList: true, subtree: true })
|
|
|
|
// Stop watching for changes after we finish loading
|
|
window.addEventListener('load', () => observer.disconnect())
|
|
}
|
|
|
|
observeScripts()
|
|
}
|
|
|
|
const script = document.createElement('script')
|
|
script.id = 'metastreaminitscript'
|
|
script.textContent = `(${mainWorldScript}());`
|
|
if (document.documentElement) {
|
|
document.documentElement.appendChild(script)
|
|
}
|
|
})()
|