chrome-extension-archive/AutoplayStopper/content/content.js

320 lines
16 KiB
JavaScript
Raw Permalink Normal View History

2021-02-10 18:28:04 +00:00
const ContentScript = new function() {
"use strict";
const start = Date.now();
const any = (window.wrappedJSObject ? "-moz" : "-webkit") + "-any";
const frameSelector = `iframe:${any}(:not([src]), [src^='javascript:' i], [src^='about:blank' i]):not([flashstopped])`;
var data = {debug: false};
const _trace = function TRACE(format, ...etc) {
if (!data.debug) return TRACE._noop || (TRACE._noop = function(){});
return console.log.bind(console, "### " + format + " ###", ...etc);
};
var selector = null;
var handler = null; //loadURL(chrome.extension.getURL("script/handler.js"));
var href = frameElement !== null ? frameElement.src || "about:blank" : location.href;
var ret = 0;
_trace("ready(%s) content.js - iframe: %s href: %s loc: %s fs: %s")(start - Date.now(),
!!frameElement && frameElement.id, href == location.href || href, location.href, window.flashstopped);
if (window.flashstopped) return;
window.flashstopped = true;
chrome.runtime.sendMessage("permission", handlePermission);
function handlePermission(response) {
if (chrome.runtime.lastError && ret <= 3) {
console.error(`handlePermission - error ret: ${ret} msg: ${chrome.runtime.lastError.message}`);
window.setTimeout(function() { chrome.runtime.sendMessage("permission", handlePermission)}, ret++ * 250);
};
if (!response) return;
data = response.data;
selector = response.selector;
handler = `${response.uhandler}\n\n${response.handler}`;
if (!response.allow) load(window);
addEventListener("message", function(e) {
if (e.data && e.data.id == "userinput") e.stopImmediatePropagation();
}, true);
};
if (parent && parent != window)
parent.postMessage({id: "userinput"}, "*");
function load({window, frameElement, location})
{
var href = frameElement !== null ? frameElement.src || "about:blank" : location.href;
_trace("loading(%s) content.js - iframe: %s href: %s loc: %s")(start - Date.now(),
!!frameElement && frameElement.id, href == location.href || href, location.href);
var match = 0, query = 0;
var injected = false;
var body = window.document.body;
var userInput = null;
var n = handleDocument();
_trace("body: %s n: %s")(!!body, n);
var docObserver = new MutationObserver(function(mutations) {
_trace("!!! docObserver - body: %s neb: %s !!!")(!!body, body != window.document.body);
if (window.document.body && body != window.document.body) {
if (body) observer.observe(window.document.body, { subtree: true, childList: true});
body = window.document.body;
requestAnimationFrame(function() {
var n = handleDocument();
_trace(">>>>>>> requestAnimationFrame n: %s <<<<<<<")(n);
});
window.setTimeout(function() {
var n = handleDocument();
_trace(">>>>>>> setting body observer - n: %s <<<<<<<<")(n);
}, 500);
var n = handleDocument();
_trace("!!! docObserver - out n: %s !!!")(n);
}
});
var observer = new MutationObserver(function(mutations) {
for (var i = 0; i < mutations.length; i++)
for (var j = 0; j < mutations[i].addedNodes.length; j++) {
var addedNode = mutations[i].addedNodes[j];
if (addedNode.nodeType === 1){
match++;
if (addedNode.children.length) query++;
if (handleNodesDeep(addedNode, true)) {
_trace(">>>>>>> MutationObserver: tag: %s id: %s <<<<<<<")(addedNode.localName, addedNode.id);
}
var frames = addedNode.matches(frameSelector) ? [addedNode] : [];
if (frames.push(...addedNode.querySelectorAll(frameSelector)))
handleFrames(frames);
}
}
});
2021-03-10 22:27:34 +00:00
if (window.document.body)
2021-02-10 18:28:04 +00:00
observer.observe((href.search("^https?:") == -1) ? window.document : window.document.body
, { subtree: true, childList: true});
2021-03-10 22:27:34 +00:00
else if (window.document.documentElement) {
2021-02-10 18:28:04 +00:00
docObserver.observe(window.document.documentElement, { childList: true});
2021-03-10 22:27:34 +00:00
}
else {
return;
}
// workaround for chrome no events on blank iframe ...
setTimeout( function() {
2021-02-10 18:28:04 +00:00
window.addEventListener("mousedown", onContextMenu, true);
["mousedown","click"].forEach((a) => window.addEventListener(a, function(e) {
if (e.isTrusted && e.button == 0) {
userInput = {e: e, time: Date.now()};
if (injected) dispatchUserInput(userInput);
}
}, true));
if (data.debug) chrome.storage.local.get(["selector-css"], function(data) {
window.document.head.appendChild(document.createElement("style"))
.textContent = data["selector-css"];
});
}, 1000);
window.addEventListener("DOMContentLoaded", function() {
observer.observe(window.document.body, { subtree: true, childList: true});
var n = handleDocument();
_trace(">>>>>>> DOMContentLoaded n: %s match: %s query: %s iframe: %s loc: %s <<<<<<<")
(n , match, query, !!frameElement && frameElement.id, location.href);
}, true);
window.addEventListener("message", function(e) {
if (!e.data || e.data.id != "userinput") return;
if (e.source == window.parent) {
_trace("@@@ handleMessage(userinput) @@@@")();
var si = new MouseEvent("siminput", Object.assign({view: window}, e.data));
userInput = {e: Object.defineProperty(si, "target", {value: window}), time: e.data.time};
if (injected) dispatchUserInput(userInput);
}
if (e.source && e.source.parent == window && userInput) {
var iframe, iframes = [...window.document.querySelectorAll("iframe")];
if (userInput.e.target.shadowRoot) iframes.push(...userInput.e.target.shadowRoot.querySelectorAll("iframe"));
if (iframe = iframes.find((a) => a.contentWindow == e.source)) {
var ue = userInput.e, time = userInput.time, r = iframe.getBoundingClientRect();
var data = {id: "userinput", clientX: ue.pageX - r.left - scrollX, clientY: ue.pageY - r.top - scrollY, time};
e.source.postMessage(data, "*");
_trace("@@@ iframe.postMessage(userinput) @@@")();
}
}
}, true);
function handleDocument()
{
2021-03-10 22:27:34 +00:00
var body = (window.document.body || window.document.documentElement);
if (body) {
var nodes = handleNodesDeep(body);
var frames = body.querySelectorAll(frameSelector);
if (frames.length) handleFrames(frames);
return nodes;
}
return null;
2021-02-10 18:28:04 +00:00
};
function handleNodes(nodes, delayed)
{
if (!injected) {
if (href.search("^https?:") == -1 && !delayed)
return window.setTimeout(function(){ handleNodes(nodes, true)});
injected = loadScript();
if (userInput) dispatchUserInput(userInput);
}
nodes.forEach(function(node) {
node.setAttribute("flashstopped", true);
try { node.parentNode.setAttribute("flashstopped_p", true)} catch(e){};
if (!node.flashstopped) {
node.dispatchEvent(new Event("flashstop:bind", {bubbles: true, composed: true}));
chrome.runtime.sendMessage("count");
_trace("match: %s query: %s")(match, query);
}
node.flashstopped = true;
});
};
function handleFrames(frames)
{
if (!window.wrappedJSObject) frames.forEach(function(frame) {
if (!frame.contentWindow) return;
_trace("!!! iframe - id: %s src: %s fs: %s !!!")(frame.id, frame.src, frame.contentWindow.flashstopped);
if (!frame.contentWindow.flashstopped) {
frame.setAttribute("flashstopped", true);
frame.contentWindow.flashstopped = true;
frame.contentWindow.Function('parent.postMessage({id: "userinput"}, "*")')();
window.setTimeout(function(){ load(frame.contentWindow)});
};
});
};
2021-03-10 22:27:34 +00:00
2021-02-10 18:28:04 +00:00
function handleNodesDeep(node, match)
{
2021-03-10 22:27:34 +00:00
if (!node) return null;
2021-02-10 18:28:04 +00:00
var nodes = match && (node.matches(selector) || node.shadowRoot) ? [node] : [];
var count = nodes.push(...node.querySelectorAll(selector));
for (var el of nodes.filter((a) => a.shadowRoot)) {
nodes.splice(nodes.indexOf(el), 1)[0].setAttribute("flashstopped", true);
count += handleNodesDeep(el.shadowRoot) - 1;
observer.observe(el.shadowRoot, { subtree: true, childList: true});
}
if (nodes.length) handleNodes(nodes);
return count;
};
function loadScript()
{
var code = `(function(data){ ${_trace} ${Handlers} ${handler} (${init})()})(${JSON.stringify(data)})`;
var script = window.document.createElement("script");
script.textContent = code;
if (window.wrappedJSObject)
script.textContent += "\ndocument.currentScript.dispatchEvent(new Event('load'));";
script.onload = function() { this.executed = true};
(window.document.head || window.document.documentElement).appendChild(script);
script.remove();
if (window.wrappedJSObject && !script.executed) // fff
try { window.eval(code); } catch(e) {};
return true;
};
// initilaizer & adapter for the injection code...
function init()
{
var handlingUserInput = false
var lastUserInput = null;
var handlers = new Handlers(TRACE);
function isHandlingUserInput() { return handlingUserInput = handlingUserInput && Date.now() - lastUserInput.time < 100 };
registerUserHandlers(handlers, TRACE, isHandlingUserInput, () => TRACE("setHandlingUserInput")(), () => lastUserInput, data);
registerHandlers(handlers, TRACE, isHandlingUserInput, () => TRACE("setHandlingUserInput")(), () => lastUserInput, data);
window.addEventListener("userinput", function(e) {
handlingUserInput = true;
setTimeout(function() { handlingUserInput = false;});
lastUserInput = {e: Object.assign({target: e.target}, e.detail.e), time: e.detail.time};
}, true);
window.addEventListener("flashstop:bind", function onbind(e){
var aElement = e.target, path = e.composedPath();
if (path && path[0] != aElement) aElement = path[0];
TRACE("onElementBinding - tag: %s id: %s loc: %s")(aElement.localName, aElement.id, window.location.href);
window.wrappedJSObject = window;
try {
handlers.apply(wrapper(aElement));
} catch(e) {
aElement.addEventListener("playing", () => {
if (!lastUserInput || Date.now() - lastUserInput.time > 1500) aElement.pause();
})};
delete window.wrappedJSObject;
}, true);
function wrapper(node)
{
var proxy = new Proxy(node, { get: function(target, prop, receiver) {
var proto = Object.getPrototypeOf(node);
var res = Reflect.get(proto, prop, node);
return typeof res == "function" && res.bind ? res.bind(node) : res;
}, set: function(target, prop, value, receiver) {
var proto = Object.getPrototypeOf(node);
return Reflect.set(proto, prop, value, Reflect.has(proto, prop) ? node : receiver);
}});
return Object.create(proxy, {wrappedJSObject: { value: node}});
};
TRACE("addEventListener - flashstop:bind")();
};
function inRect(x, y, el){ var b = el.getBoundingClientRect(); return (x > b.left && x < b.right && y > b.top && y < b.bottom) ? el : null};
function matchDeep(el, x, y, res) {
if (!el.shadowRoot) return el.matches("video, audio") ? el : null;
for (var n of [...el.shadowRoot.elementsFromPoint(x, y)].filter((a) => a.getRootNode().host == el))
if (res = matchDeep(n, x, y)) return res;
};
function getMediaElement(aWin, aPrev, x, y)
{
try {aWin.document} catch(e) { _trace("getMediaElement - %s")(e); return };
var res, sx = x + aWin.mozInnerScreenX, sy = y + aWin.mozInnerScreenY, round = Math.round;
_trace("sc(%s,%s) win(%s,%s) win: %s prev: %s")(round(sx), round(sy), round(x), round(y), aWin.location.host, aPrev && aPrev.location.host);
for (var node of aWin.document.elementsFromPoint(x, y))
if (res = (node.localName != "iframe" || node.contentWindow == aPrev) ? matchDeep(node, x, y) :
getMediaElement(node.contentWindow, null, x - node.getBoundingClientRect().left, y - node.getBoundingClientRect().top))
return (res.localName != "iframe" && _trace("getMediaElement - success id: %s")(res.id), res);
if (res = [...aWin.document.querySelectorAll("video:not([src^='data:'])")].find((node) => inRect(x, y, node)))
return (_trace("getMediaElement - success id: %s")(res.id), res);
return (aPrev && aWin != aWin.parent && aWin.frameElement) && getMediaElement(aWin.parent, aWin, x +
aWin.frameElement.getBoundingClientRect().left, y + aWin.frameElement.getBoundingClientRect().top);
};
function onContextMenu(e)
{ // before
if (e.isTrusted && e.button == 2){
var media = e.target instanceof HTMLMediaElement ? e.target : getMediaElement(e.view, e.view, e.clientX, e.clientY);
var paused = media && media.paused;
if (media) {
chrome.runtime.sendMessage({ msg: "contextmenu", media: true, paused },
function(){ if (!chrome.runtime.lastError) paused ? media.play() : media.pause(); });
if (e.ctrlKey)
e.stopImmediatePropagation();
}
else
chrome.runtime.sendMessage({ msg: "contextmenu", media: false});
}
};
};
};
function dispatchUserInput({e, time, e: {view: window}})
{ // fff!
with(e) var Obj = window.Object, data = Object.assign(new Obj, {clientX, clientY, pageX, pageY});
var target = e.target != window && window.document.contains(e.target) ? e.target : window;
target.dispatchEvent(new CustomEvent("userinput", {detail: Object.assign(new Obj,{e: data, time})}));
};
// </>