Greasy Fork

Picarto Polyfills for older browsers like FF52

Restore some compatibility between Picarto and older browsers (like FF 52) -- StevenRoy

目前为 2024-10-19 提交的版本。查看 最新版本

// ==UserScript==
// @name        Picarto Polyfills for older browsers like FF52
// @namespace   http://michrev.com/
// @description Restore some compatibility between Picarto and older browsers (like FF 52) -- StevenRoy
// @include     https://www.picarto.tv/*
// @include     https://picarto.tv/*
// @include     https://www.picarto.tv
// @include     https://picarto.tv
// @version     2.001
// @run-at document-start
// @grant       GM_xmlhttpRequest
// ==/UserScript==
"use strict";

//     _____________  __    __
//    / ___________/ /  \,-' /
//   / /__    ___   / /\,-/ /
//   \___ \  / __\ / /   / /
//______/ / / /   / /   / /
//_______/ /_/   /_/   /_/



// This is what "spread syntax" in object literals looks like:
// {...r, r:e, g:t, b:n}
// { ...methods, r, g, b }
// But because that feature doesn't exist in the latest FF for 32-bit Windows, it needs to be converted.
// (It didn't exist until 2018 and every major browser stopped updating long before then. Screw them.)

// These examples become:
// Object.assign({},r,{r:e, g:t, b:n})
// Object.assign({},methods,{ r, g, b })

// There's a similar feature for arrays but -that- is already supported! Go figure.

function grabscript(url){
	console.log("Fetching:",url);
	var response=GM_xmlhttpRequest({
		method: "GET",
		url: url,
		synchronous: true // Imagine trying to make this script without this feature.
	});
	if (!response.responseText) { throw("Something stupid happened while trying to load:",url,response); }
	console.log('Script fetched, length:'+response.responseText.length);
	return response.responseText;
}

// This adds (actually re-adds) a script to the current page so it can run.
function addScript(text) {
	var newScript = document.createElement('script');
	newScript.type = "text/javascript";
	newScript.textContent = text;
	var head = document.getElementsByTagName('head')[0];
//	console.log("We're adding the thing, length:",text.length);
	head.appendChild(newScript);
//	console.log('We added the thing, length:',text.length);
	return newScript;
}

unsafeWindow.String.prototype.trimStart=unsafeWindow.String.prototype.trimLeft;
unsafeWindow.String.prototype.trimEnd=unsafeWindow.String.prototype.trimRight;
unsafeWindow.URL.canParse=exportFunction(function canParse(url) {
    var base = arguments.length < 2 || arguments[1] === undefined ? undefined : arguments[1];
    try {
      return !!new URL(url, base);
    } catch (error) {
      return false;
    }
  },unsafeWindow);


// So, here's the deal:

// On other pages, I could just use beforescriptexecute to intercept problem scripts,
// fix the syntax error with a single responseText.replace(), then re-add it.

// In this case, the script I need to intercept isn't loaded like a script!
// When something is loaded using the Worker object, beforescriptexecute does NOTHING!

// So I tried to edit the Worker constructor, using ES6 subclasses and exportFunction
// to create a version of the object that could load, edit and inject the fixed code. That was hard...

// I came so dang close to that actually working, too, but started running into
// Error: Permission denied to access property "addEventListener"
// ...Seems pages can't access Worker objects loaded from blobs created in userscripts
// because of some "same-origin security" bull.
// I spent so many frustrating hours trying to find a way around that...

// Finally I decided, I had to try a different approach, and I got creative:

window.addEventListener('beforescriptexecute', function(e) {
	var src = e.target.src;
	console.log("Checking script:"+(src?src:"(unknown)"));
	if (src && src.search(/\/static\/js\/1\.[0-9a-f]+\.chunk\.js/) != -1) { // static/js/1.b2dade89.chunk.js
		console.log('Intercepted probable chat script'); // This is the script that loads the Worker
		var onl=e.target.onload; // There's a callback when the script loads. We gotta keep the callback when we replace the script!
//		console.log('onload:',onl);
		e.target.onload=e.target.onerror=null; // Prevent the onerror handler when we cancel this.
		e.preventDefault();
		e.stopPropagation();
		var scr=grabscript(src); // load the script so we can edit it...
		// Original code: _=new Worker("/chatworker.min.js?ver=".concat(m.l)),
console.log('grabbed, adding');
		scr=addScript("window.AAA=1;console.log('Running edited script');\n"+scr.replace(/new Worker/g,"editedWorker")+"\n\n"+
// And now that script has the function that does the loading, editing, and fixing of the other script...
		'function editedWorker(n){console.log("Fetching worker: "+n);var d=new XMLHttpRequest(); d.open("GET","https://picarto.tv"+n,false); d.send(null);'+
'console.log("Fetch status:"+d.status);'+
			'd=d.responseText.replace(/\\{\\.\\.\\.([^,}]+)(?:,([^}]+))?\\}/g,(m,p1,p2)=>{return "Object.assign({},"+p1+(p2? ",{"+p2+"}" :"")+")";})'+
			'.replace(/importScripts\\("([a-z.]+)"\\)[,;]/g,(m,p1)=>{'+
				'return \'importScripts("https://picarto.tv/\'+p1+\'");\'; // absolute paths required here for some stupid reason\n'+
			'});'+

			'return new Worker(URL.createObjectURL(new Blob(["console.log(\'Fixed worker running - SrM was here\');\\n"+d ])));'+
		'}');
console.log('added');
		if (window.AAA) { onl(); } else { scr.onload=scr.onerror=onl; } // New script, same callback
	}
});