Add AutoplayStopper v1.8.7

This commit is contained in:
Michael Campagnaro 2021-02-10 13:28:04 -05:00
parent f5620cbc84
commit 69eb447c28
25 changed files with 2772 additions and 0 deletions

View File

@ -0,0 +1,5 @@
This is an extracted copy of https://chrome.google.com/webstore/detail/autoplaystopper/ejddcgojdblidajhngkogefpkknnebdh version 1.8.7. I then loaded it into Chrome as an unpacked extension.
This was done to bypass auto extension updates that may eventually be injected with malware.
The extraction was done using the "Chrome extension source viewer" addon (https://github.com/Rob--W/crxviewer).
I reviewed the code for malicious changes and made other small tweaks.

View File

@ -0,0 +1,201 @@
{
"extensionDescription": {
"message": "Stops video autoplay gracefully."
},
"allowSite": {
"message": "Allow autoplay for %S"
},
"allowSession": {
"message": "Allow session autoplay for %S"
},
"disableSite": {
"message": "Block flash detection for %S"
},
"allowAll": {
"message": "Disable everywhere"
},
"settings": {
"message": "Settings"
},
"version": {
"message": "Version"
},
"autoplayTabLabel": {
"message": "Autoplay"
},
"permissions": {
"message": "Websites Permissions:"
},
"manageExceptions": {
"message": "Exceptions..."
},
"defaultMode": {
"message": "Default Mode:"
},
"disableAutoplay": {
"message": "Block Autoplay"
},
"allowAutoplay": {
"message": "Allow Autoplay"
},
"flashTabLabel": {
"message": "Flash"
},
"allowFlash": {
"message": "Allow Detection"
},
"disableFlash": {
"message": "Block Detection"
},
"autoplayExceptions": {
"message": "Autoplay exceptions"
},
"flashExceptions": {
"message": "Flash detection exceptions"
},
"exceptionHostnameHeader": {
"message": "Hostname"
},
"exceptionBehaviorHeader": {
"message": "Behavior"
},
"exceptionAllow": {
"message": "Allow"
},
"exceptionBlock": {
"message": "Block"
},
"exceptionPrompt": {
"message": "Block (strict)"
},
"exceptionSession": {
"message": "Session"
},
"exceptionUndefined": {
"message": "Undefined"
},
"apply": {
"message": "Apply"
},
"removeAll": {
"message": "Remove All"
},
"done": {
"message": "Done"
},
"play": {
"message": "Play"
},
"pause": {
"message": "Pause"
},
"selector": {
"message": "Selector"
},
"text": {
"message": "Text"
},
"reset": {
"message": "Reset"
},
"script": {
"message": "Script"
},
"userScript": {
"message": "User Script"
},
"path": {
"message": "Path"
},
"actions": {
"message": "Actions"
},
"edit": {
"message": "Edit"
},
"load": {
"message": "Load"
},
"export": {
"message": "Export..."
},
"setPath": {
"message": "Set Path"
},
"selectFile": {
"message": "File..."
},
"advanced": {
"message": "Advanced"
},
"devtools": {
"message": "Devtools Panel"
},
"overwrite": {
"message": "Disable overwrite on update"
},
"debug": {
"message": "Log debug info"
},
"loadError": {
"message": "Load script error: "
},
"allowFileUrls": {
"message": "Error: Cannot use user script without permission to access file URLs."
},
"fileNotFound": {
"message": "AutoplayStopper - error: File not found.\n Check that the script folder is in the workspace and verify the path."
},
"evalError": {
"message": "AutoplayStopper - exception: "
}
}

View File

@ -0,0 +1,186 @@
"use strict";
var Background = new function() {
console.log("loading background.js ...");
const {js, css} = chrome.runtime.getManifest().content_scripts[0];
const badgeColor = "#646464";
const playMsg = chrome.i18n.getMessage("play");
const pauseMsg = chrome.i18n.getMessage("pause");
const sArea = "local";
const permsAutoplayKey = Permissions.key("autoplay");
const permsAutoplayDefaultKey = Permissions.defaultKey("autoplay");
const permsFlashKey = Permissions.key("flash");
const permsFlashDefaultKey = Permissions.defaultKey("flash");
const sKeys = ["disabled", "debug", permsFlashKey, permsFlashDefaultKey, permsAutoplayKey,
permsAutoplayDefaultKey, "selector-css", "uhandler", "handler", "disableOverwrite", "devtools"];
var selector = null;
var permissions = null;
var permsAutoplayCache = {};
var permsFlashCache = {};
var tabs = { get(id) { return this[id] || (this[id] = {id, count: 0})}};
var menuitem = null;
var storage = new Storage(sKeys, sArea);
storage.addChangeListener(function(changes){
if (changes.indexOf("selector-css") != -1) selector = storage.data["selector-css"].split("{")[0];
if (changes.indexOf(permsAutoplayKey) != -1 || changes.indexOf(permsAutoplayDefaultKey) != -1) permsAutoplayCache = {};
if (changes.indexOf(permsFlashKey) != -1 || changes.indexOf(permsFlashDefaultKey) != -1) permsFlashCache = {};
if (changes.indexOf("disabled") != -1) updateIcon();
});
storage.ready.then(function() {
console.log("storage.ready");
updateIcon();
permissions = new Permissions(storage.data);
if (permissions.default("autoplay") === undefined) permissions.setDefault("autoplay", Permission.DENY_ACTION);
if (permissions.default("flash") === undefined) permissions.setDefault("flash", Permission.ALLOW_ACTION);
chrome.webNavigation.onCommitted.addListener(onCommitted, {url: [{schemes: ["http", "https", "about"]}]});
if (!storage.data.uhandler)
loadScript("uhandler", chrome.extension.getURL("script/userhandler.js"));
if (!storage.data.handler)
loadScript("handler", chrome.extension.getURL("script/handler.js"));
if (!storage.data["selector-css"])
loadScript("selector-css", chrome.extension.getURL("script/selector.css"));
selector = storage.data["selector-css"].split("{")[0];
js.forEach(function(a) { execScript(a, null, 0)});
});
chrome.browserAction.setBadgeBackgroundColor({color: badgeColor});
chrome.tabs.onRemoved.addListener(function(tabid){ delete tabs[tabid]; });
chrome.tabs.onActivated.addListener(resetMenuitem);
chrome.runtime.onMessage.addListener(handleMessage);
chrome.runtime.onInstalled.addListener(function(details) {
if (details.reason == "update" && !storage.data.disableOverwrite) {
loadScript("handler", chrome.extension.getURL("script/handler.js"));
loadScript("selector-css", chrome.extension.getURL("script/selector.css"));
selector = storage.data["selector-css"].split("{")[0];
}
});
return {
get i18n() { return i18n; },
get storage() { return storage; },
get permissions() { return permissions; },
openOptionsPage: function openOptionsPage(hash) {
chrome.runtime.openOptionsPage();
if (hash) addEventListener("message", function(e) {
if (location.origin == e.origin && e.data == "optionsPageActive")
e.source.location.hash = hash;
}, { once: true});
}
};
function resetMenuitem(){ if (menuitem) menuitem = chrome.contextMenus.remove("play-menuitem"); }
function updateIcon() { chrome.browserAction.setIcon({ path: "/icons/" + (storage.data.disabled ? "icon32d.png" : "icon32.png")})};
function handleMessage(request, sender, sendResponse)
{
if (!storage.data.handler) {
console.log("handleMessage delayed -", request, sender);
storage.ready.then(function() { handleMessage(request, sender, sendResponse)});
return true;
}
if (request == "permission") {
var data = {debug: !!storage.data.debug};
var allow = true, host = sender.tab && new URL(sender.tab.url).origin;
if (host && !storage.data.disabled) {
var permission = permsAutoplayCache[host] ||
(permsAutoplayCache[host] = permissions.testPermission("autoplay", sender.tab.url));
allow = permission == Permission.ALLOW_ACTION ||
(permission == Permission.ACCESS_SESSION && tabs.get(sender.tab.id).last == host);
data.strict = permission == Permission.PROMPT_ACTION;
}
var msg = {msg: "permission", data, allow, selector, handler: storage.data.handler, uhandler: storage.data.uhandler};
// console.log("background send:", msg, sender.url, sender.tab.url);
sendResponse(msg);
}
if (request == "count")
chrome.browserAction.setBadgeText({text: String(++tabs.get(sender.tab.id).count), tabId: sender.tab.id});
if (request.msg == "contextmenu") {
resetMenuitem();
if (request.media)
menuitem = chrome.contextMenus.create({ id: "play-menuitem", title: request.paused ? playMsg : pauseMsg,
contexts: ["all"], onclick: function onClick(info, tab) { if (info.menuItemId == "play-menuitem") sendResponse();}
})
return !!request.media;
}
if (request.msg == "select") {
fetch(request.url, {cache: "no-store"}).then(function(res) {
res.blob().then(function(file){ sendResponse({url: URL.createObjectURL(file), name: file.name}); });
});
return true;
}
if (request.msg == "load") {
var res = loadScript(request.script, request.file);
sendResponse(res);
}
};
function onCommitted(details)
{
var allow = true, url = new URL(details.url);
if (url.protocol.search("^chrome") != -1) return;
var tab = tabs.get(details.tabId);
if (details.frameId == 0) {
tab.last = tab.host;
tab.host = url.origin;
tab.url = details.url;
tab.count = 0;
chrome.browserAction.setBadgeText({text:'', tabId: tab.id});
chrome.tabs.query({ active: true, currentWindow: true}, function(tabs) {
if (tabs[0] && tabs[0].id == tab.id) resetMenuitem();
});
}
if (!storage.data.disabled && tab.url) {
var permission = permsFlashCache[tab.host] ||
(permsFlashCache[tab.host] = permissions.testPermission("flash", tab.url));
if (permission == Permission.DENY_ACTION)
execScript("content/navigator.js", details.tabId, details.frameId);
}
};
function execScript(file, tabId, frameId) {
chrome.tabs.executeScript(tabId, {file, frameId, matchAboutBlank: true, runAt: "document_start"}, function() {
if (chrome.runtime.lastError && storage.data.debug)
console.error("chrome.tabs.executeScript - error", chrome.runtime.lastError, {tabId, frameId, file});
});
};
function insertCSS(file, tabId, frameId) {
chrome.tabs.insertCSS(tabId, {file, frameId, matchAboutBlank: true, runAt: "document_start"}, function() {
if (chrome.runtime.lastError && storage.data.debug)
console.error("chrome.tabs.insertCSS - error", chrome.runtime.lastError, {tabId, frameId, file});
});
};
function loadScript(script, url){
try {
var request = new XMLHttpRequest();
request.overrideMimeType("text/plain");
request.open("GET", url, false);
request.setRequestHeader("pragma", "no-cache");
request.send();
if (request.statusText == "Not Found" || !request.responseURL)
throw new DOMException(`Failed to load '${url}'.`, "NetworkError");
storage.data[script] = request.responseText;
storage.commit([script]);
} catch (e) {
chrome.extension.isAllowedFileSchemeAccess(function(allowed) {
if(!allowed && url.search(/^blob:/) == -1)
return alert(chrome.i18n.getMessage("allowFileUrls"));
alert(`${chrome.i18n.getMessage("loadError")} ${e.name}\n ${e.message}`);
});
return e;
}
};
};
//</>

View File

@ -0,0 +1,310 @@
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);
}
}
});
if (window.document.body)
observer.observe((href.search("^https?:") == -1) ? window.document : window.document.body
, { subtree: true, childList: true});
else
docObserver.observe(window.document.documentElement, { childList: true});
// workaround for chrome no events on blank iframe ...
setTimeout( function() {
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()
{
var nodes = handleNodesDeep(window.document.body || window.document.documentElement);
var frames = (window.document.body || window.document.documentElement).querySelectorAll(frameSelector);
if (frames.length) handleFrames(frames);
return nodes;
};
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)});
};
});
};
function handleNodesDeep(node, match)
{
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})}));
};
// </>

View File

@ -0,0 +1,49 @@
// handler-utils.js See license.txt for terms of usage and credits
"use strict";
/**
* Handlers - content handlers manager...
* ----------------------------------------
* @bYO!
*/
function Handlers(TRACE){
var _handlers = [], _mapRemoved = new Map();
return {
unload: function unload(){
for (var win of _mapRemoved.keys()) {
win.removeEventListener("unload", onUnload, false);
TRACE("Handlers.unload - remove wnd loc: %s")(win.location);
};
_handlers = [];
_mapRemoved = new Map();
},
add: function add(handler) { _handlers.push(handler); },
remove: function remove(win, handler) {
if (!_mapRemoved.has(win)){
win.addEventListener("unload", onUnload, false);
_mapRemoved.set(win, []);
}
_mapRemoved.get(win).push(handler);
TRACE("Handlers.remove - handler: %s")(handler.name);
},
apply: function apply(element){
var removed = _mapRemoved.get(element.ownerDocument.defaultView);
for (var handler, i = 0; i < _handlers.length; i++)
if ((handler = _handlers[i]) && (!removed || removed.indexOf(handler) == -1))
try {
if (handler(element)) break;
} catch (e) { TRACE("!!!!!!!!! handler: %s Exception: %s !!!!!!!!!")(handler.name, e); };
},
get length() { return _handlers.length; }
};
function onUnload(e){
var win = e.target.defaultView;
TRACE("Handlers.onUnload - remove wnd has: %s loc: %s")(_mapRemoved.has(win), win.location);
_mapRemoved.delete(win);
}
};

View File

@ -0,0 +1,39 @@
"use strict";
(function() {
// setTimeout(function() {
var script = document.createElement("script");
script.textContent = `(${setNavigator})()`;
(document.head || document.documentElement || document).appendChild(script);
script.remove();
// });
function setNavigator(nav = { plugins: navigator.plugins, mimeTypes: navigator.mimeTypes}) {
console.log("setNavigator - uri: " + location);
var plugins = Object.assign(Object.create(PluginArray.prototype), [...nav.plugins]);
Object.defineProperties(plugins, { item: {value: (a) => plugins[a]}, namedItem: {value: (a) => plugins[a]},
refresh: {value: () => { nav.plugins.refresh(); setNavigator(nav)}},
length: { writable: true, value: nav.plugins.length}});
for (var a of plugins) { Object.defineProperty(plugins, a.name, {configurable: true, value: a}); };
[].splice.call(plugins, [].findIndex.call(plugins, (a) => a.name == "Shockwave Flash"), 1);
delete plugins["Shockwave Flash"];
Object.defineProperty(navigator, "plugins", {configurable: true, value: plugins});
var mimetypes = Object.assign(Object.create(MimeTypeArray.prototype), [...nav.mimeTypes]);
Object.defineProperties(mimetypes, { item: {value: (a) => mimetypes[a]}, namedItem: {value: (a) => mimetypes[a]},
refresh: {value: () => { plugins.refresh()}}, length: { writable: true, value: nav.mimeTypes.length}});
for (var a of mimetypes) { Object.defineProperty(mimetypes, a.type, {configurable: true, value: a}); };
[].forEach.call(nav.plugins["Shockwave Flash"] || [], ({type}) =>
delete mimetypes[[].splice.call(mimetypes, [].findIndex.call(mimetypes, (a) => a.type == type), 1)[0].type]);
Object.defineProperty(navigator, "mimeTypes", {configurable: true, value: mimetypes});
};
})();
// </>

144
AutoplayStopper/devtools.js Normal file
View File

@ -0,0 +1,144 @@
"use strict";
const DevTools = new function() {
const sArea = "local";
const sKeys = ["debug", "devtools", "codefile", "ucodefile", "codefilename", "ucodefilename", "disableOverwrite", "selector-css", "uhandler", "handler"];
var storage = new Storage(sKeys, sArea);
storage.ready.then(function() {
if (!storage.data.devtools)
return;
// devtools page - create panel ...
if (location.search == "?view=page")
return chrome.devtools.panels.create("AutoplayStopper", "/icons/icon48.png", "/skin/devtools.html?view=panel");
// devtools panel ...
var fileinput = Object.assign(document.createElement("input"), {type: "file", hidden: true});
document.body.appendChild(fileinput);
document.readyState == "complete" ? load() : addEventListener("load", load);
function load() {
i18n.process(document);
document.getElementById("ver").textContent = chrome.runtime.getManifest().version;
setCheckbox("overwrite", "disableOverwrite");
setCheckbox("debug");
setSelector();
setup("");
setup("u");
};
function setSelector()
{
var selector = document.getElementById("selector");
var apply = document.getElementById("apply");
selector.value = storage.data["selector-css"];
selector.oninput = function() { apply.disabled = selector.value == storage.data["selector-css"]; };
apply.onclick = function() {
storage.data["selector-css"] = selector.value;
storage.commit(["selector-css"]);
apply.disabled = true;
};
document.getElementById("reset").onclick = function() {
var url = chrome.extension.getURL("script/selector.css");
chrome.runtime.sendMessage({ msg: "load", script: "selector-css", file: url });
};
storage.addChangeListener(function(changes){
if (changes.includes("selector-css")) selector.value = storage.data["selector-css"];
apply.disabled = true;
});
};
function setup(pfx)
{
var file = pfx + "codefile";
var filename = file + "name";
document.getElementById(pfx + "path-input").textContent = storage.data[file];
document.getElementById(pfx + "filename").textContent = storage.data[filename];
updateState();
document.getElementById(pfx + "load-button").onclick = function() {
var url = storage.data[file] || chrome.extension.getURL("script/") + (pfx && "user") + "handler.js";
chrome.runtime.sendMessage({ msg: "load", script: pfx + "handler", file: url});
};
document.getElementById(pfx + "export-button").onclick = function() {
var a = document.getElementById("export-link");
a.download = (pfx && "user") + "handler.js";
a.href = "data:application/javascript," + encodeURIComponent(storage.data[pfx + "handler"]);
a.click();
};
document.getElementById(pfx + "edit-button").onclick = function() {
var open = chrome.devtools.panels.openResource || function(){};
open(getURL(file), null, function(res){
if (res.isError && res.code == "E_NOTFOUND")
alert(chrome.i18n.getMessage("fileNotFound"));
});
};
var committed = chrome.devtools && chrome.devtools.inspectedWindow.onResourceContentCommitted;
committed && committed.addListener(function(resource){
if (resource.url == getURL(file))
resource.getContent(function(content, encoding) {
chrome.devtools.inspectedWindow.eval(content, function(result, isException) {
if (isException)
alert(chrome.i18n.getMessage("evalError") + isException.value);
});
});
});
document.getElementById(pfx + "path-input").oninput = updateState;
document.getElementById(pfx + "path-button").onclick = function(e) {
storage.data[filename] = document.getElementById(pfx + "filename").textContent = "";
storage.data[file] = document.getElementById(pfx + "path-input").textContent;
storage.commit([file, filename]);
updateState();
};
document.getElementById(pfx + "select-button").onclick = function(e) {
fileinput.onchange = function() {
if (!chrome.extension.getBackgroundPage) { // fff...
var data = {msg: "select", url: URL.createObjectURL(this.files[0])};
chrome.runtime.sendMessage(data, function(res) { updateSelected(res.url, res.name); });
}
else {
var url = chrome.extension.getBackgroundPage().URL.createObjectURL(this.files[0]);
updateSelected(url, this.files[0].name);
}
};
fileinput.click();
};
function updateSelected(url, name) {
storage.data[file] = document.getElementById(pfx + "path-input").textContent = url;
storage.data[filename] = document.getElementById(pfx + "filename").textContent = name;
storage.commit([file, filename]);
updateState();
};
function updateState()
{
var a = document.getElementById(pfx + "path-input").textContent == (storage.data[file] || "");
document.getElementById(pfx + "path-button").disabled = a;
document.getElementById(pfx + "load-button").disabled = !a;
document.getElementById(pfx + "edit-button").disabled = !a || !storage.data[file];
};
};
function getURL(file) { try { return new URL(storage.data[file]).href } catch(e) {}; };
function setCheckbox(id, key = id)
{
var node = document.getElementById(id);
node.checked = !!storage.data[key];
node.onchange = function() {
storage.data[key] = this.checked;
storage.commit([key]);
};
};
});
};
//</>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,53 @@
{
"description": "[MODIFIED BY ME] Stops video autoplay gracefully.",
"manifest_version": 2,
"name": "AutoplayStopper",
"version": "1.8.7",
"default_locale": "en",
"icons": {
"32": "icons/icon32.png",
"48": "icons/icon48.png"
},
"permissions": [
"http://*/*", "https://*/*",
"tabs",
"contextMenus",
"storage",
"webNavigation"
],
"background": {
"scripts": ["utils.js", "background.js"]
},
"browser_action": {
"default_icon": {
"32": "icons/icon32.png"
},
"default_title": "AutoplayStopper",
"default_popup": "skin/popup.html",
"browser_style": true
},
"content_scripts": [
{
"run_at": "document_start",
"all_frames": true,
"match_about_blank": true,
"matches": ["http://*/*", "https://*/*"],
"js": ["content/handler-utils.js", "content/content.js"]
}],
"options_ui": {
"chrome_style": true,
"browser_style": true,
"page": "skin/options.html"
},
"devtools_page": "skin/devtools.html?view=page",
"optional_permissions": ["file://*/*"]
}

292
AutoplayStopper/options.js Normal file
View File

@ -0,0 +1,292 @@
"use strict";
const Options = new function() {
const {i18n, storage, permissions: perms} = chrome.extension.getBackgroundPage().Background;
const permsAutoplayKey = perms.key("autoplay");
const permsAutoplayDefaultKey = perms.defaultKey("autoplay");
const permsFlashKey = perms.key("flash");
const permsFlashDefaultKey = perms.defaultKey("flash");
const data = { flash: { type: "flash", dirty: false}, autoplay: { type: "autoplay", dirty: false}};
const { autoplay, flash } = data;
var tabId = null;
var selected = null;
// chrome.tabs.getCurrent(function(tab) { tabId = tab.id; }); // chrome workaround ...
chrome.tabs.query({ active: true, currentWindow: true}, function(tabs) { tabId = tabs[0].id; });
chrome.tabs.onActivated.addListener(function(info){ if (tabId == info.tabId) postActive(); });
addEventListener("load", init);
addEventListener("hashchange", function() { updateState(location.hash.slice(1)); });
function init()
{
i18n.process(document);
var listener = storage.addChangeListener(function(changes){
if (changes.indexOf(permsAutoplayKey) != -1) {
autoplay.exceptions.init(perms.entries("autoplay"));
autoplay.dirty = false;
updateApply();
}
if(changes.indexOf(permsAutoplayDefaultKey) != -1) {
document.getElementById("autoplay-default").value = perms.default("autoplay");
}
if (changes.indexOf(permsFlashKey) != -1) {
flash.exceptions.init(perms.entries("flash"));
flash.dirty = false;
updateApply();
}
if(changes.indexOf(permsFlashDefaultKey) != -1) {
document.getElementById("flash-default").value = perms.default("flash");
}
});
addEventListener("unload", function(){ storage.removeChangeListener(listener); });
var manifest = chrome.runtime.getManifest();
document.getElementById("extension-version").appendChild(document.createTextNode(manifest.version));
document.getElementById("autoplay-default").value = perms.default("autoplay");
document.getElementById("flash-default").value = perms.default("flash");
document.getElementById("devtools").checked = storage.data.devtools;
autoplay.exceptions = new ExceptionsList(document.getElementById("autoplay-list"));
autoplay.exceptions.init(perms.entries("autoplay"));
document.getElementById("autoplay-default").addEventListener("change", function(e) {
perms.setDefault("autoplay", e.target.value);
storage.commit([permsAutoplayDefaultKey]);
});
flash.exceptions = new ExceptionsList(document.getElementById("flash-list"));
flash.exceptions.init(perms.entries("flash"));
document.getElementById("flash-default").addEventListener("change", function(e) {
perms.setDefault("flash", e.target.value);
storage.commit([permsFlashDefaultKey]);
});
document.querySelectorAll(".exceptions-list-button").forEach(function(button) {
button.onclick = function(e) { location.hash = `${button.getAttribute("contenttype")}`; };
});
autoplay.exceptions.addDirtyListener(function(d) { autoplay.dirty = d; updateApply(); });
flash.exceptions.addDirtyListener(function(d) { flash.dirty = d; updateApply(); });
document.querySelector(".close-button").onclick = function() { location.hash = ""; };
document.getElementById("exceptions-clear").onclick = function() { selected.exceptions.clear(); };
document.getElementById("exceptions-apply").onclick = apply;
document.getElementById("exceptions-confirm").onclick = function() { apply(); location.hash = ""; };
document.getElementById("devtools").onclick = function(e) {
storage.data.devtools = this.checked;
storage.commit(["devtools"]);
};
if (location.hash) updateState(location.hash.slice(1));
postActive();
};
function postActive() { chrome.extension.getBackgroundPage().postMessage("optionsPageActive", "*"); };
function updateApply() { document.getElementById("exceptions-apply").disabled = !selected || !selected.dirty; };
function apply()
{
if (selected.dirty) {
perms.clear(selected.type);
for (var [url, perm] of selected.exceptions.entries()) { perms.set(selected.type, url, perm); };
storage.commit([perms.key(selected.type)], function(err){
if (err) { selected.dirty = true; };
updateApply();
});
selected.dirty = false;
}
};
function updateState(state)
{
// console.log(`updateState(${state})`);
if (state) {
document.querySelector("div[contenttype]:not([hidden])").hidden = true;
document.querySelector("h2[contenttype]:not([hidden])").hidden = true;
document.querySelector(`div[contenttype=${state}]`).hidden = false;
document.querySelector(`h2[contenttype=${state}]`).hidden = false;
var overlay = document.querySelector(".overlay");
overlay.hidden = false;
setTimeout(function() { overlay.classList.remove("transparent"); }, 20);
selected = data[state];
updateApply();
}
else {
var overlay = document.querySelector(".overlay");
overlay.classList.add("transparent");
setTimeout(function() { overlay.hidden = true; }, 500);
}
}
};
const ExceptionsList = function ExceptionsList(list){
var itemid = 0;
var baseitem = list.querySelector("#listitem");
var inputitem = list.querySelector("#inputitem");
var selected = list.querySelector("[selected]");
var listeners = [];
var dirty = false;
baseitem.remove();
list.addEventListener("mousedown", handleMousedown.bind(this));
list.addEventListener("click", handleClick.bind(this));
list.addEventListener("keydown", handleKeyDown.bind(this));
list.addEventListener("change", handleChange.bind(this));
list.addEventListener("focus", function(e) {
if (!e.target.classList.contains("row-delete-button")) {
list.setAttribute("has-element-focus", "hasElementFocus");
handleSelection(e.target.closest("[role=listitem]"));
if (e.target.getAttribute("displaymode") == "static") e.target.nextSibling.focus();
}
}, true);
list.addEventListener("blur", function(e) {
if (!list.contains(e.relatedTarget) && !e.target.classList.contains("row-delete-button")) {
list.removeAttribute("has-element-focus");
handleSelection();
if (!e.relatedTarget) list.parentNode.closest("[tabindex]").focus();
}
}, true);
return {
init: function init(entries) {
this.clear();
entries.sort(function(a, b) { return a[0].localeCompare(b[0]); });
for (var [key, type] of entries) { appendItem(key, type); };
dirty = false;
},
entries: function * entries()
{
for (var node of list.children)
if (node.id.search("listitem") == 0 && node != inputitem)
yield [node.querySelector("#perms-host").textContent, parseInt(node.querySelector("#perms-type-select").value)];
},
clear: function clear()
{
for (var node of [].slice.apply(list.children))
if (node.id.search("listitem") == 0 && node != inputitem) node.remove();
inputitem.querySelector("#perms-host-input").focus();
fireDirty(true);
},
get dirty() { return dirty; },
addDirtyListener: function(listener) { listeners.push(listener); },
removeDirtyListener: function(listener) { var idx = listeners.indexOf(listener); if (idx != -1) listeners.splice(idx); }
};
function appendItem(host, type, scroll)
{
var item = baseitem.cloneNode(true);
item.id = item.id + "-" + (++itemid);
item.querySelector("#perms-host-input").value = item.querySelector("#perms-host").textContent = host;
var select = item.querySelector("#perms-type-select");
select.value = type;
if (type != 0) item.querySelector("#perms-type").textContent = select[select.selectedIndex].text;
list.insertBefore(item, inputitem);
if (scroll) scrollToBottom(inputitem);
};
function scrollToBottom(node)
{
if (node.clientHeight < node.scrollHeight)
return node.scrollTop = node.scrollHeight - node.clientHeight;
if (node.parentNode) scrollToBottom(node.parentNode);
};
function handleMousedown(e)
{
var target = e.target;
var item = target.closest("[role=listitem]");
if (target.classList.contains("row-delete-button")) return;
if (target.id == "perms-host") {
var x = e.clientX, y = e.clientY, offset = document.caretPositionFromPoint ?
document.caretPositionFromPoint(x, y).offset : document.caretRangeFromPoint(x, y).startOffset;
var input = item.querySelector("#perms-host-input");
var inside = x < inputitem.querySelector("#perms-host-input").getBoundingClientRect().right;
input.selectionStart = input.selectionEnd = inside ? 0 : input.value.length;
setTimeout(function() { input.selectionStart = input.selectionEnd = offset; }, 25);
}
if (item && !target.hasAttribute("tabindex") && !item.hasAttribute("editing")) {
item.querySelector("[id^=perms-host]").focus();
e.preventDefault();
};
};
function handleSelection(item)
{
if (selected && item != selected) {
selected.removeAttribute("lead");
selected.removeAttribute("editing");
if (item) selected.removeAttribute("selected");
if (selected == inputitem) handleInput(!item);
selected.querySelectorAll("[id^=perms]").forEach(function(a) { a.tabIndex = item ? -1 : 0; });
}
if (item && !item.hasAttribute("editing")) {
item.setAttribute("selected", "selected");
item.setAttribute("lead", "lead");
item.setAttribute("editing", "");
item.querySelectorAll("[id^=perms]")
.forEach(function(a) { a.tabIndex = a.getAttribute("displaymode") == "static" ? -1 : 0; });
selected = item;
}
};
function handleClick(e)
{
var target = e.target;
if (target.classList.contains("row-delete-button")) {
var item = target.closest("[role=listitem]");
if (item == selected) handleSelection(inputitem);
item.remove();
list.focus({preventScroll: true});
fireDirty(true);
return;
};
};
function handleKeyDown(e)
{
if (selected && selected != inputitem && e.target.id != "perms-host-input" && e.keyCode == 46)
selected.querySelector(".row-delete-button").click();
if (selected && e.key == "Enter") selected == inputitem ? handleInput(true) : list.focus({preventScroll: true});
if (selected && e.key == "ArrowUp" && e.target.id != "perms-type-select" && selected != list.querySelector("[role=listitem]"))
selected.previousSibling.querySelector("[id^=perms-host]").focus();
if (selected && e.key == "ArrowDown" && e.target.id != "perms-type-select" && selected != inputitem)
selected.nextSibling.querySelector("[id^=perms-host]").focus();
};
function handleChange(e)
{
var target = e.target;
var item = target.closest("[role=listitem]");
if (item != inputitem) {
if (target.id == "perms-type-select") {
item.querySelector("#perms-type").textContent =
target.value != 0 ? target[target.selectedIndex].text : "";
fireDirty(true);
}
if (target.id == "perms-host-input"){
try {
var url = new URL(target.value);
target.value = url.origin;
item.querySelector("#perms-host").textContent = target.value;
fireDirty(true);
}
catch (e) { target.value = item.querySelector("#perms-host").textContent; };
}
}
};
function handleInput(scroll)
{
try {
var target = inputitem.querySelector("#perms-host-input")
var url = new URL(target.value);
target.value = url.origin;
appendItem(target.value, inputitem.querySelector("#perms-type-select").value, scroll);
target.value = null;
fireDirty(true);
}
catch (e) {};
};
function fireDirty(d) { dirty = d; for (var listener of listeners) listener(d); };
};
// </>

94
AutoplayStopper/popup.js Normal file
View File

@ -0,0 +1,94 @@
"use strict";
const Popup = new function(){
const start = Date.now();
const {i18n, storage, permissions: perms, openOptionsPage} = chrome.extension.getBackgroundPage().Background;
while (Date.now() - start < 50);
setTimeout(function() {
while (Date.now() - start < 150);
addEventListener("unload", function() {
if (Date.now() - start < 250) {
storage.data.disabled = !storage.data.disabled;
storage.commit(["disabled"]);
}
});
});
const permsAutoplayKey = perms.key("autoplay");
const permsFlashKey = perms.key("flash");
var url = null;
var ready = new Promise( function(resolve) {
chrome.tabs.query({ active: true, currentWindow: true}, function(tabs) {
url = new URL(tabs[0].url);
resolve();
})
});
addEventListener("load", function() { ready.then(init); });
function init()
{
i18n.process(document);
var permission = perms.testPermission("autoplay", url);
var permissionFlash = perms.testPermission("flash", url);
initItem( document.getElementById("allow_site"), !!permission.origin && permission != perms.ACCESS_SESSION && permission, [url.origin]);
initItem( document.getElementById("allow_session"), !!permission.origin && permission == perms.ACCESS_SESSION, [url.origin]);
initItem( document.getElementById("disable_site"), !!permissionFlash.origin && permissionFlash, [url.origin]);
initItem( document.getElementById("allow_all"), storage.data.disabled);
initItem( document.getElementById("settings"));
document.getElementById("popupmenu")
.classList.toggle("restricted", url.protocol.search("^http") == -1 || !!storage.data.disabled);
window.onclick = function(e) {
switch (e.target.id) {
case "settings":
openOptionsPage();
close();
break;
case "allow_site":
if ( [undefined, perms.UNKNOWN_ACTION].indexOf(perms.get("autoplay", url)) != -1
&& permission != perms.default("autoplay")) { openOptionsPage("#autoplay"); close(); break; };
perms.set("autoplay", url, (permission == perms.ALLOW_ACTION) ? perms.DENY_ACTION : perms.ALLOW_ACTION);
storage.commit([permsAutoplayKey]);
init();
break;
case "allow_session":
if ( [undefined, perms.UNKNOWN_ACTION].indexOf(perms.get("autoplay", url)) != -1
&& permission != perms.default("autoplay")) { openOptionsPage("#autoplay"); close(); break; };
(permission == perms.ACCESS_SESSION) ? perms.remove("autoplay", url) :
perms.set("autoplay", url, perms.ACCESS_SESSION);
storage.commit([permsAutoplayKey]);
init();
break;
case "disable_site":
if ( [undefined, perms.UNKNOWN_ACTION].indexOf(perms.get("flash", url)) != -1
&& permissionFlash != perms.default("flash")) { openOptionsPage("#flash"); close(); break; };
perms.set("flash", url, (permissionFlash == perms.ALLOW_ACTION) ? perms.DENY_ACTION : perms.ALLOW_ACTION);
storage.commit([permsFlashKey]);
init();
break;
case "allow_all":
storage.data.disabled = !storage.data.disabled;
storage.commit(["disabled"]);
init();
break;
}
};
};
function initItem(item, check, args)
{
if (check !== undefined) check ? item.setAttribute("checked", check) : item.removeAttribute("checked");
if (args) { var i = 0; item.textContent = item.textContent.replace(/%S/g, function () { return args[i++]; }); };
};
};
//</>

View File

@ -0,0 +1,289 @@
"use strict";
function registerHandlers(handlers, TRACE, isHandlingUserInput, setHandlingUserInput, lastUserInput, data = {})
{
var dummyid = Math.floor(Math.random() * 100);
function testUserInput(el, delay)
{
var win = el.ownerDocument.defaultView, _last = lastUserInput(win), e = _last && _last.e;
if (!e || Date.now() - _last.time > delay) return false;
var px = e.pageX - win.scrollX, py = win.scrollY ? e.pageY - win.scrollY : e.clientY, r = el.getBoundingClientRect(), {width ,height} = r;
for (var i = 0; i < 5 && el.parentNode && (r.top + win.scrollY < 0 || r.left + win.scrollX < 0 || r.width < 10 || r.height < 10); i++) r = (el = el.parentNode).getBoundingClientRect();
r.width = Math.max(r.width, width), r.height = Math.max(r.height, height);
return px >= r.left && px <= r.right && py >= r.top && py <= r.bottom;
};
// htmlmedia
handlers.add(function handleHtmlMedia(aElement){
const simEvents = {play: 0, playing: 50, pause: 350}, eventTypes = Object.keys(simEvents), props = ["preload","autoplay","poster","setAttribute","removeAttribute"];
const document = aElement.ownerDocument, window = document.defaultView;
if (aElement.localName == "video" || aElement.localName == "audio")
{
aElement.id || (aElement.id = "dummyid" + (++dummyid % 100));
TRACE('handleElement html5 media - tag: %s id: %s autoplay: %s preload: "%s" poster: %s paused: %s')(aElement.localName, aElement.id, aElement.autoplay, aElement.preload, !!aElement.poster, aElement.paused);
if (aElement.wrappedJSObject.play !== window.wrappedJSObject.HTMLMediaElement.prototype.play) TRACE('handleElement html5 media - %s.play() != HTMLMediaElement.play !!!')(aElement.id);
// Yendifplayer .autoplay = 1 => ...
var autoplay = aElement.autoplay || !aElement.paused, count = 0, released = false;
Object.defineProperties(aElement.wrappedJSObject, { preload: {configurable: true, set: (a) => TRACE('%s.preload = "%s"')(aElement.id, a)/* || (aElement.preload = "metadata")*/, get: () => aElement.preload}
, autoplay: {configurable: true, set: (a) => TRACE("%s.autoplay = %s")(aElement.id, a) || (a && aElement.wrappedJSObject.play()), get: () => aElement.autoplay}});
aElement.wrappedJSObject.setAttribute = (a, b) => (props.indexOf(a) != -1) ? TRACE("%s.setAttribute(%s, %s)")(aElement.id, a, b) || (aElement.wrappedJSObject[a] = b): aElement.setAttribute(a, b);
aElement.wrappedJSObject.removeAttribute = (a) => (props.indexOf(a) != -1) ? TRACE("%s.removeAttribute(%s)")(aElement.id, a) : aElement.removeAttribute(a);
function simPlay(e) {
e && aElement.removeEventListener(e.type, simPlay);
if (aElement.simPlay !== false && aElement.paused)
eventTypes.forEach((key) => window.setTimeout(() => !released && (TRACE("simPlay - %s.%s")(aElement.id, key) || aElement.dispatchEvent(new window.Event(key))), simEvents[key]));
};
aElement.wrappedJSObject.play = () => {
var force = aElement.allowPlay || testUserInput(aElement, 5000);
var userInput = isHandlingUserInput(window);
TRACE("%s.play(%s) - force: %s state: %s user: %s")(aElement.id, count, !!force, aElement.readyState, userInput);
if (!userInput && !force) {
count++ == 0 && (aElement.preload = "metadata") && (aElement.simPlay || aElement.readyState >= 2 ? simPlay() : aElement.addEventListener("loadeddata", simPlay));
return count < 5 ? Promise.reject(new window.DOMException("The play method is not allowed by the user agent.", "NotAllowedError")) : simPlay() || Promise.resolve();
}
released = !data.strict && delete aElement.wrappedJSObject.play; // jwplayer: zapiks.fr rottentomatoes baeblemusic
force && !userInput && setHandlingUserInput(window);
return aElement.play();
};
window.wrappedJSObject.Object.defineProperty(aElement.wrappedJSObject.play, "toString", window.Object.assign(window.Object(), {value: () => "[native code]"}));
aElement.autoplay = false;
aElement.preload != "none" && (aElement.preload = "metadata"); // mediaelement.js
!data.strict && aElement.addEventListener("play", function cleanup(e){ e.isTrusted && !aElement.paused && (aElement.removeEventListener("play", cleanup), aElement.muted = false,
TRACE("%s cleanup...")(aElement.id), props.forEach((prop) => delete aElement.wrappedJSObject[prop]), delete aElement.wrappedJSObject.play)});
autoplay && aElement.wrappedJSObject.play(); // flowplayer
!aElement.paused ? !released && aElement.pause() : window.setTimeout(() => !released && aElement.pause(), 0); // dbtv.no html5box
};
});
// simplay
const hosts = [{host: "cnn.com$", val: true}, {host: "twitch.tv$"}, {host: "yastatic.net$"},
{host: "bbc.(com|co.uk)$"}, {host: "(yahoo|yimg).com$", class: "html5-video", val: true}, {class: "\\b(video-js|vjs-tech)\\b"}];
handlers.add(function handleSimplay(aElement){
if (aElement.localName == "video")
for (var a of hosts) if (aElement.ownerDocument.location.hostname.match(a.host) && aElement.className.match(a.class)) aElement.simPlay = !!a.val;
});
// YouTube
handlers.add(function handleYouTube(aElement){
const document = aElement.ownerDocument, window = document.defaultView, jsWin = window.wrappedJSObject;
if (document.location.hostname.search("\.youtube(-nocookie)?\.com$") != -1)
{
TRACE("handleElement ytplayer - tag: %s player: %s")(aElement.localName, aElement.id);
if (document.location.search.search("feature=youtube-anywhere-player") == -1)
{
function stopPlayer(ytplayer){
if (testUserInput(ytplayer, 5000)) return !TRACE("ytplayer released...")();
var vid = ytplayer.getVideoData && ytplayer.getVideoData().video_id;
TRACE("ytplayer stopPlayer(%s) - videoId: %s t: %s")(ytplayer.id || "", vid, ytplayer.getCurrentTime());
ytplayer.cueVideoById(vid, ytplayer.getCurrentTime());
var nop = false, _seekTo = ytplayer.seekTo, _playVideo = ytplayer.playVideo;
ytplayer.playVideo = function doNothing() { nop || (nop = testUserInput(ytplayer, 1000)) ? _playVideo.apply(this) : TRACE("@@@@@@ ytplayer doNothing @@@@@")()};
ytplayer.seekTo = function seekTo(tm) { nop || (nop = testUserInput(ytplayer, 1000)) ? _seekTo.apply(this, arguments) : ytplayer.cueVideoById(vid, tm), TRACE("ytplayer seekTo(%s)")(tm); };
function onstate(a) { if (a == 1) { nop = true; ytplayer.playVideo = _playVideo; ytplayer.seekTo = _seekTo; TRACE("ytplayer - removed doNothing")(); ytplayer.removeEventListener("onStateChange", onstate); }};
ytplayer.addEventListener("onStateChange", onstate);
}
if (aElement.localName == "video" && aElement.parentNode.classList.contains("html5-video-container")) {
if (!data.strict && aElement.parentNode.parentNode.id == "movie_player")
window.setTimeout(() => aElement.allowPlay = true, document.location.search.search("feature=youtube-anywhere-player") == -1 ? 5000 : 0);
aElement.simPlay = false;
if (aElement.parentNode.parentNode.classList.contains("html5-video-player"))
window.setTimeout(() => stopPlayer(aElement.wrappedJSObject.parentNode.parentNode));
}
}
return true;
}
});
// googleads
handlers.add(function handleGoogleAds(aElement){
const document = aElement.ownerDocument, window = document.defaultView;
var host = aElement.getRootNode().host;
if (host && host.localName == "lima-video") {
TRACE("handleElement googleads - tag: %s")(aElement.localName);
window.addEventListener("visibilitychange", (e) => e.stopPropagation(), true);
aElement.wrappedJSObject.addEventListener("play", function(){ if (!testUserInput(this, 100)) this.pause()}, {once: true});
return true;
}
});
// ifunny.co
handlers.add(function handleIFunny(aElement){
const document = aElement.ownerDocument, window = document.defaultView;
if (document.location.hostname.match("ifunny.co$") && aElement.paused) {
(isHandlingUserInput(window) || testUserInput(aElement.parentNode, 1000)) ? aElement.play() : aElement.src = "";
return true;
}
});
// msn.com
handlers.add(function handleMsn(aElement){
const document = aElement.ownerDocument, window = document.defaultView, jsWin = window.wrappedJSObject;
if (document.location.hostname.search(/\.msn\.com$/) != -1)
{
var obj = aElement.wrappedJSObject;
if (aElement.className.indexOf("vxFlashPlayer") != -1 && aElement.localName == "embed"){
obj.MsnVideoCallback = function(msg) { msg == "playVideo" ? delete obj.MsnVideoCallback : obj.__proto__.MsnVideoCallback.apply(obj, arguments)};
var flashvars = (aElement.getAttribute("flashvars") || "").replace(/(&ap=)true/gi, "$1false")
aElement.setAttribute("flashvars", flashvars);
TRACE("handleElement msnplayer - tag: %s id: %s ")(aElement.localName, aElement.id);
return true;
}
}
});
// BBC
handlers.add(function handleBBC(aElement){
const document = aElement.ownerDocument, window = document.defaultView, jsWin = window.wrappedJSObject;
if (jsWin.embeddedMedia && jsWin.embeddedMedia.players)
for (var player of jsWin.embeddedMedia.players || []) if (player._swf && player._swf.id == aElement.id) {
TRACE("handleElement BBC - embeddedMedia.players id: %s autoplay: %s")(player._swf.id, player._settings.autoplay);
Object.defineProperty(player._settings, "autoplay", {get: () => false});
player.setData = (info) => ( info.data && (info.data.autoPlayFirstItem = false), player.__proto__.setData.call(player, info));
window.setTimeout(function(){ player._settings.autoplay = true; handlers.remove(window, handleBBC); }, 2500);
return true;
};
});
// metacafe
handlers.add(function handleMetacafe(aElement){
if (aElement.localName == "object" && (aElement.data || "").search(/s.mcstatic.com\/Flash\/.*\.swf/) != -1)
{
var param = aElement.querySelector("#" + aElement.id + ">param[name=flashvars]");
param.value = param.value.replace(/&beacons=.*(?=&)/,"");
var id = param.value.match(/itemID=([^&]*)(?=&)/)[1];
var data = aElement.data;
aElement.data = "http://www.metacafe.com/fplayer/" + id + "/.swf";
function onClick(aEvent){
aEvent.stopImmediatePropagation();
aElement.removeEventListener(aEvent.type, onClick, true);
aElement.data = data;
}
aElement.addEventListener("mouseup", onClick, true);
TRACE("handleElement metacafe - data: %s")(aElement.data);
return true;
}
});
// jwplayer
handlers.add(function handleJWPlayer(aElement){ // return;
const document = aElement.ownerDocument, window = document.defaultView, jsWin = window.wrappedJSObject;
aElement.id || (aElement.id = "dummyid" + (++dummyid % 100));
if (jsWin.jwplayer && !aElement.querySelector("[id='" + aElement.id + "']>param[name=flashvars]"))
{
if (testUserInput(aElement, 5000)) return !TRACE("jwplayer released...")();
function closest(node, selector) { while(node && !node.matches(selector)) node = node.parentElement; return node};
var res, ar, player = jsWin.jwplayer(aElement).config ?
jsWin.jwplayer(aElement) : (res = closest(aElement, ".jwplayer")) ? jsWin.jwplayer(res) : null;
if (player && (player.config || (player.config = player.getConfig())) && player.config.autostart != false)
{
TRACE("handleElement jwplayer setup - id: %s autostart: %s")(player.id, player.config.autostart);
player.config.autoStart = player.config.autostart = false;
(ar = player.config.aspectratio) && ar.indexOf("%") != -1 && (player.config.aspectratio = "100:" + ar.slice(0,-1));
aElement.localName == "video" && window.setTimeout(() => aElement.src = "");
player.setup(player.config);
}
TRACE("handleElement jwplayer - id: %s config: %s")((player && player.id), !!(player && player.config));
return player && player.config;
}
});
// flowplayer
handlers.add(function handleFlowplayer(aElement){ //return;
aElement.id || (aElement.id = "dummyid" + (++dummyid % 100));
var param = aElement.querySelector("[id='" + aElement.id + "']>param[name=flashvars]")
var config, flashvars = param ? param.value : aElement.getAttribute("flashvars");
if (flashvars && flashvars.search(/^config={/) != -1)
if ((config = JSON.parse(flashvars.slice(7).replace(/'/g,'"').replace(/("autoPlay":)true/g,"$1false"))) && (config.playlist || config.clip))
{
var playlist = (config.playlist = (config.playlist || [config.clip]));
(typeof playlist[0] == 'string') && (playlist[0] = {url: playlist[0]});
playlist[0].autoPlay = (typeof playlist[0].url == 'string' && playlist[0].url.search(/(.png|.jpg)/) != -1);
(playlist[0].autoBuffering && (playlist[0].autoBuffering = false)) || (playlist[0].autoPlay && playlist[1] && (playlist[1].autoPlay = false));
flashvars = "config=" + JSON.stringify(config);
param ? param.value = flashvars : aElement.setAttribute("flashvars", flashvars);
if (aElement.data) aElement.data = aElement.data;
TRACE("handleElement flowplayer - id: %s flashvars: %s")(aElement.id, flashvars);
return true;
}
});
// aol
handlers.add(function handleAol(aElement){
if (aElement.localName == "object" && (aElement.data || "").search(/cdn(-ssl)?.vidible.tv\/.*\.swf/) != -1)
{
aElement.id || (aElement.id = "dummyid" + (++dummyid % 100));
var param = aElement.querySelector("[id='" + aElement.id + "']>param[name=flashvars]");
if (param)
{
param.value = param.value.replace(/(initialization%22%3A%22)autoplay/, "$1click");
aElement.wrappedJSObject.doPlay = function() { TRACE("doNothing - doPlay")()};
aElement.ownerDocument.defaultView.setTimeout(() => {delete aElement.wrappedJSObject.doPlay; TRACE("handleElement aol - removed doPlay")()}, 5000);
return !TRACE("handleElement aol - data: %s")(aElement.data);
}
}
});
// general
var colValues = {"true":"false", "1":"0", "yes":"no", "on":"off", "y":"n", "Y":"N"}
function replace(match,p0,p1,p2) {
var res = p0 + (!p1 && p2 && colValues[p2] ? colValues[p2] : !!p1);
TRACE(">>> handleElement replace - match: %s p0: %s p1: %s p2: %s res: %s <<<")(match, p0, p1, p2, res);
return res;
}
function modifyParams(str) { return (str || "").replace(/((no)?auto_?(?:play|start|run)\w*(?:["']?\s?[=:]\s?["']?|%22%3A(?!\W)))(true|1|yes|y|on|null|)(?=\W|$)/gi, replace)};
function handleGeneral(aElement){
if (aElement.localName != "object" && aElement.localName != "embed")
return;
var data = modifyParams(aElement.getAttribute("data"));
if (data != aElement.getAttribute("data") && data != "")
{
aElement.setAttribute("data", data);
TRACE("handleElement - %s.data: %s")(aElement.localName, data);
}
var flashvars = modifyParams(aElement.getAttribute("flashvars")); // cnn | (remove) neulion nhl embed
if (aElement.localName == "embed" )
{
aElement.setAttribute("menu","true");
if (flashvars == aElement.getAttribute("flashvars") || flashvars == "" )
{
flashvars += "&autoplay=false&autostart=false&autoPlay=0"; // youtube(autoplay) | ustream live
aElement.setAttribute("play","false"); // basic flash
}
aElement.setAttribute("flashvars", flashvars);
if (aElement.src)
{
var src = modifyParams(aElement.src); // embed dailymotion
if (src != aElement.src) {
aElement.src = src;
var parent = aElement.parentNode, next = aElement.nextSibling; // workaround for chrome embed.src =
if (aElement.src && parent) { aElement.remove(); parent.insertBefore(aElement.wrappedJSObject, next); };
TRACE("handleElement embed.src: %s")(src);
}
}
}
aElement.id || (aElement.id = "dummyid" + (++dummyid % 100)); // [id =''] works with numeric ids!!!
var param = aElement.querySelector("[id='" + aElement.id + "']>param[name=flashvars], [id='" + aElement.id + "']>param[name=FlashVars], [id='" + aElement.id + "']>param[name=flashVars]");
if (param)
{
flashvars = modifyParams(param.value); // ustream(autoplay=false) | bbc / metacafe.embed(no) / tv.com(vid)
if (flashvars == param.value) {
if (flashvars.search(/auto(_?play(vid)?|start|run)["']?\s?[=:]/i) != -1) return; // false already exists...
flashvars += "&autoplay=false&autoPlay=0&autoStart=0&_autoPlay=no&auto_start=off"; // justin.tv(autoPlay=0)|nba.com(autostart)|espn(!autoplay)|discovery(_autoPlay=no)|56.com(auto_start)
}
param.value = flashvars;
if (aElement.data) aElement.data = aElement.data;
}
(param = aElement.querySelector("[id='" + aElement.id + "']>param[name=play]")) && (param.value = false);
if (param && aElement.data) aElement.data = aElement.data;
TRACE("handleElement - id: %s %s.flashvars: %s")(aElement.id, aElement.localName, flashvars);
};
handlers.add(handleGeneral);
};

View File

@ -0,0 +1,24 @@
object[classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"]:not([flashstopped]),
object[classid*=":d27cdb6e-ae6d-11cf-96b8-444553540000"]:not([flashstopped]),
object[codebase*="swflash.cab"]:not([flashstopped]),
object[data*=".swf"]:not([flashstopped]),
object[type="application/x-shockwave-flash"]:not([flashstopped]),
object[src*=".swf"]:not([flashstopped]),
embed[type="application/x-shockwave-flash"]:not([flashstopped]),
embed[src*=".swf"]:not([flashstopped]),
embed[allowscriptaccess]:not([flashstopped]),
embed[flashvars]:not([flashstopped]),
embed[wmode]:not([flashstopped]),
lima-video:not([flashstopped]),
/*--------- !(html5media) ---------*/
*:not([flashstopped_p]) > video,
video:not([flashstopped]),
audio:not([flashstopped]) {
border: 5px red solid !important;
box-sizing: border-box;
}
*[flashstopped] {
border: 3px blue solid !important;
box-sizing: border-box;
}

View File

@ -0,0 +1,9 @@
// userhandler.js - handler that doesn't get overwritten by new versions
"use strict";
function registerUserHandlers(handlers, TRACE)
{
// add your handlers here...
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="12px" viewBox="0 0 24 12" fill="#757575"><g><path d="M 0 0 L 24 0 L 12 12 z"/></g></svg>

After

Width:  |  Height:  |  Size: 150 B

View File

@ -0,0 +1,157 @@
<!DOCTYPE html>
<html>
<head>
<script src="/utils.js"></script>
<script src="/devtools.js"></script>
<style type="text/css">
body {
margin: 0.5% 10%;
font-family: "Segoe UI", Tahoma, sans-serif;
color: #4c4c4c;
}
label.settings-label {
min-width: 80px;
font-size: initial;
font-weight: lighter;
}
div.settings-row {
margin: 6px 18px;
display: flex;
align-items: center;
}
input.settings-checkbox {
align-self: flex-end;
-webkit-margin-start: 0px;
-webkit-margin-end: 10px;
margin-inline-start: 0px;
margin-inline-end: 10px;
}
button {
-webkit-margin-end: 3px;
white-space: nowrap;
}
button:disabled {
color: #a0a0a0;
}
button:not([disabled]) {
color: #4c4c4c;
}
span.input {
min-width: 450px;
max-width: 85%;
border: 1px solid rgb(170, 170, 170);
padding: 1px 2px;
-webkit-appearance: textfield;
font-size: 13.3333px;
white-space: nowrap;
overflow: hidden;
height: 17px; /* fff */
}
hr {
border-color: rgb(210, 210, 210);
border-top-width: 0px;
margin-bottom: 1em;
margin-top: .9em;
}
h2 {
font-weight: 400;
margin-top: 15px; /* fff */
}
#splitter {
display: flex;
flex-flow: column nowrap;
overflow: hidden;
height: 0;
min-height: calc(40px + 6.5em);
max-height: calc(100vh - 10px);
margin-bottom: 1px;
resize: vertical;
background: linear-gradient(to right bottom, transparent 50%,
rgba(0, 0, 0, 0.1) 50%) 100% 100% / 16px 15px no-repeat;
}
#selector {
height: 100%;
width: 800px;
color: darkslateblue;
border: 1px solid rgb(170, 170, 170);
resize: none;
}
:focus {
outline: rgb(170, 170, 170) auto 1px;
}
</style>
</head>
<body>
<div id="splitter">
<div class="settings-row" style="margin:0 0px;">
<img src="../icons/icon48.png" style="margin: 10px; height: 32px;" />
<h2>AutoplayStopper</h2>
<h2 id="ver" style="font-weight: 200; -webkit-margin-start: 10px; margin-inline-start: 10px;">1.0.0</h2>
</div>
<hr style="width: calc(100% - 2px); margin-top: 6px; margin-bottom: 0px"/>
<h2 i18n-content="selector">Selector</h2>
<div class="settings-row" style="height: 100%;">
<label class="settings-label" i18n-content="text" style="margin-bottom: auto;">Text</label>
<textarea id="selector" spellcheck="false"></textarea>
</div>
<div class="settings-row" style="margin-bottom: calc(.9em - 1px);">
<label class="settings-label" i18n-content="actions">Actions</label>
<button id="reset" i18n-content="reset">Reset</button>
<button id="apply" disabled="" i18n-content="apply"></button>
</div>
</div>
<hr style="margin-top:0px"/>
<h2 i18n-content="script">Script</h2>
<div class="settings-row">
<label class="settings-label" i18n-content="path">Path:</label>
<span id="path-input" class="input" contenteditable="true" spellcheck="false"></span>
</div>
<div class="settings-row">
<label class="settings-label" i18n-content="actions">Actions:</label>
<button id="edit-button" i18n-content="edit">Edit</button>
<button id="load-button" i18n-content="load">Load</button>
<button id="export-button" i18n-content="export">Export...</button>
<button id="path-button" i18n-content="setPath" disabled>Set Path</button>
<button id="select-button" i18n-content="selectFile" >File...</button>
<label id="filename" class="settings-label" style="-webkit-margin-start:10px;margin-inline-start:10px;"/>
</div>
<hr />
<h2 i18n-content="userScript">User Script</h2>
<div class="settings-row">
<label class="settings-label" i18n-content="path">Path:</label>
<span id="upath-input" class="input" contenteditable="true" spellcheck="false"></span>
</div>
<div class="settings-row">
<label class="settings-label" i18n-content="actions">Actions:</label>
<button id="uedit-button" i18n-content="edit">Edit</button>
<button id="uload-button" i18n-content="load">Load</button>
<button id="uexport-button" i18n-content="export">Export...</button>
<button id="upath-button" i18n-content="setPath" disabled>Set Path</button>
<button id="uselect-button" i18n-content="selectFile">File...</button>
<label id="ufilename" class="settings-label" style="-webkit-margin-start:10px;margin-inline-start:10px;"/>
</div>
<hr />
<div class="settings-row">
<input id="overwrite" class="settings-checkbox" type="checkbox" />
<label class="settings-label" i18n-content="overwrite">Disable overwrite on update</label>
</div>
<div class="settings-row">
<input id="debug" class="settings-checkbox" type="checkbox" />
<label class="settings-label" i18n-content="debug">Log debug info</label>
</div>
<a id="export-link" hidden="true"></a>
</body>
</html>

View File

@ -0,0 +1,615 @@
/* chrome://resources/css/list.css */
list,
grid {
display: block;
outline: none;
overflow: auto;
position: relative; /* Make sure that item offsets are relative to the
list. */
}
list > *,
grid > * {
-webkit-user-select: none;
background-color: rgba(255, 255, 255, 0);
border: 1px solid rgba(255, 255, 255, 0); /* transparent white */
border-radius: 2px;
cursor: default;
line-height: 20px;
margin: -1px 0;
overflow: hidden;
padding: 0 3px;
position: relative; /* to allow overlap */
text-overflow: ellipsis;
white-space: pre;
}
list > * {
display: block;
}
grid > * {
display: inline-block;
}
list > [lead],
grid > [lead] {
border-color: transparent;
}
list:focus > [lead],
grid:focus > [lead] {
border-color: hsl(214, 91%, 65%);
z-index: 2;
}
list > [anchor],
grid > [anchor] {
}
list:not([disabled]) > :hover,
grid:not([disabled]) > :hover {
background-color: hsl(214, 91%, 97%);
border-color: hsl(214, 91%, 85%);
z-index: 1;
}
list > [selected],
grid > [selected] {
background-color: hsl(0, 0%, 90%);
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8),
rgba(255, 255, 255, 0));
border-color: hsl(0, 0%, 85%);
z-index: 2;
}
list:focus > [selected],
grid:focus > [selected] {
background-color: hsl(214, 91%, 89%);
border-color: hsl(214, 91%, 65%);
}
list:focus > [lead][selected],
list > [selected]:hover,
grid:focus > [lead][selected],
grid > [selected]:hover {
background-color: hsl(214, 91%, 87%);
border-color: hsl(214, 91%, 65%);
}
list > .spacer,
grid > .spacer {
border: 0;
box-sizing: border-box;
display: block;
margin: 0;
overflow: hidden;
visibility: hidden;
}
list :-webkit-any(
input[type='input'],
input[type='password'],
input[type='search'],
input[type='text'],
input[type='url']),
list :-webkit-any(
button,
input[type='button'],
input[type='submit'],
select):not(.custom-appearance) {
line-height: normal;
margin: 0;
vertical-align: middle;
}
list input[type='text'] {
margin-left: -4px;
padding: 3px;
width: 190px;
}
/* chrome://resources/css/overlay.css */
[hidden] {
display: none !important;
}
.overlay {
-webkit-box-align: center;
-webkit-box-orient: vertical;
-webkit-box-pack: center;
-webkit-perspective: 1px;
-webkit-transition: 200ms opacity;
background-color: rgba(255, 255, 255, 0.75);
/* bottom: 50px; */
display: -webkit-box;
left: 0;
overflow: auto;
padding: 20px;
position: fixed;
right: 0;
top: 0;
}
.overlay.transparent .page {
transform: scale(0.99) translateY(-20px);
}
.transparent {
opacity: 0;
}
.overlay .page {
max-height: 335.5px;
/* display: -webkit-box;
-webkit-box-orient: vertical;
*/
-webkit-border-radius: 3px;
-webkit-box-orient: vertical;
-webkit-transition: 200ms transform;
background: white;
box-shadow: 0 4px 23px 5px rgba(0, 0, 0, 0.2), 0 2px 6px rgba(0,0,0,0.15);
color: #333;
display: -webkit-box;
min-width: 400px;
padding: 0px;
position: relative;
z-index: 0;
}
.overlay .page .content-area {
-webkit-box-flex: 1;
overflow: auto;
overflow: overlay; /* auto */
padding: 0px 20px 0px; /* 0px 17px 6px */
position: relative;
min-height: 226px;
outline: 0;
}
.overlay .page > .close-button {
background-image: url(IDR_CLOSE_DIALOG);
background-position: center;
background-repeat: no-repeat;
height: 14px;
position: absolute;
right: 7px;
top: 7px;
width: 14px;
z-index: 1;
}
.overlay .page > .close-button:hover {
background-image: url(IDR_CLOSE_DIALOG_H);
}
.overlay .page > .close-button:focus:not(:hover) {
background-image: url(IDR_CLOSE_DIALOG_H);
filter: opacity(75%);
}
div.settings-row {
margin: 2px 18px;
display: flex;
padding: 2px 0;
}
label.settings-label {
width: 130px;
display: inherit;
}
.overlay .page h2 {
padding: 4px;
}
/* chrome://settings/contentExceptions#... */
body {
position: relative;
}
.hbox {
-webkit-box-orient: horizontal;
display: -webkit-box;
}
.vbox {
-webkit-box-orient: vertical;
display: -webkit-box;
}
.box-align-center {
-webkit-box-align: center;
}
.stretch {
-webkit-box-flex: 1;
}
.raw-button,
.raw-button:hover,
.raw-button:active {
background-color: transparent;
background-repeat: no-repeat;
border: none;
box-shadow: none !important;
min-width: 0;
min-height: 0;
padding: 1px 6px;
}
.page list {
/* Min height is a multiple of the list item height (32) */
box-sizing: content-box;
min-height: 192px;
}
.option {
margin-top: 0;
}
.settings-list,
.settings-list-empty {
border: 1px solid #d9d9d9;
border-radius: 2px;
}
/* Editable List properties */
list > * {
-webkit-box-align: center;
-webkit-transition: 150ms background-color;
border: none;
border-radius: 0; /* TODO(dbeam): Is this necessary? */
box-sizing: border-box;
display: -webkit-box;
height: 32px;
margin: 0;
}
list > .spacer {
/* The above height rule should not apply to spacers. When redraw is called
on the list they will be given an explicit element height but this ensures
they have 0 height to begin with. */
height: 0;
}
list:not([disabled]) > :hover {
background-color: rgb(228, 236, 247);
}
/* Note: If this becomes the list style for other WebUI pages these rules can be
* simplified (since they wont need to override other rules). */
list:not([has-element-focus]) > [selected],
list:not([has-element-focus]) > [lead][selected] {
background-color: #d0d0d0;
background-image: none;
}
list[has-element-focus] > [selected],
list[has-element-focus] > [lead][selected],
list:not([has-element-focus]) > [selected]:hover,
list:not([has-element-focus]) > [selected][lead]:hover {
background-color: rgb(187, 206, 233);
background-image: none;
}
.settings-list[has-element-focus] > [lead],
.settings-list[has-element-focus] > [lead][selected] {
border-bottom: 1px solid rgb(120, 146, 180);
border-top: 1px solid rgb(120, 146, 180);
}
.settings-list[has-element-focus] > [lead]:nth-child(2),
.settings-list[has-element-focus] > [lead][selected]:nth-child(2) {
border-top: 1px solid transparent;
}
.settings-list[has-element-focus] > [lead]:nth-last-child(2),
.settings-list[has-element-focus] > [lead][selected]:nth-last-child(2) {
border-bottom: 1px solid transparent;
}
.settings-list[disabled] > [lead][selected],
.settings-list[disabled]:focus > [lead][selected] {
border: none;
}
list .deletable-item {
-webkit-box-align: center;
outline: none;
}
list .deletable-item > :first-child {
-webkit-box-align: center;
-webkit-box-flex: 1;
-webkit-padding-end: 5px;
-moz-padding-end: 5px;
display: -webkit-box;
}
list .row-delete-button {
-webkit-transition: 150ms opacity;
background-color: transparent;
background-image:
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAiElEQVR42r2RsQrDMAxEBRdl8SDcX8lQPGg1GBI6lvz/h7QyRRXV0qUULwfvwZ1tenw5PxToRPWMC52eA9+WDnlh3HFQ/xBQl86NFYJqeGflkiogrOvVlIFhqURFVho3x1moGAa3deMs+LS30CAhBN5nNxeT5hbJ1zwmji2k+aF6NENIPf/hs54f0sZFUVAMigAAAABJRU5ErkJggg==);
border: none;
display: block;
height: 16px;
opacity: 1;
width: 16px;
}
list > *:not(:hover):not([selected]):not([lead]) .row-delete-button,
list:not([has-element-focus]) > *:not(:hover):not([selected])
.row-delete-button,
list[disabled] .row-delete-button,
list .row-delete-button[disabled] {
opacity: 0;
pointer-events: none;
}
list .row-delete-button:hover {
background-image:
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAqUlEQVR4XqWRMQqEMBBF/1E8Ra6x6V3FRnS9QbCxtJg6Z7CzE9lTiIXXyUb3C8EULixDIMM8Zt4kcDfxM5A45U+cgeXnC1tREgkzAgob3hiq3CUHvGLG4FTQoSgxQGDrzN8WTLBGnx2IVDksen9GH7Z9hA5E6uxABMJyCHDMCEGHzugLQPPlBCBNGq+5YtpnGw1Bv+te15ypljTpVzdak5Opy+z+qf//zQ+Lg+07ay5KsgAAAABJRU5ErkJggg==) !important;
}
list .row-delete-button:active {
background-image:
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAQklEQVR4AWP4TwBSTQGDHcMZIIYAKA9VwRkwtINJgyCaCTAlCBaKAoQ+hFmoCqBKENKkK8C0gpAjCXuTyICiQ2QBAPSwyG3ByZlCAAAAAElFTkSuQmCC)
,
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAA/ElEQVR4Xu3UsWrCUBiG4efGlIBoIMFbcnYolYJ3pg4iKGrGYFTRwaUFhYAekiDt0EG++X2W83N8/3J/DbwBMJJSsdQItcDY1VlCOImzq3Ed8OmicHASB3ns5KBw8VUNpDJrW7uAiJ3sbK1l0mqArpmFTUlQ5jYWZrrUAUSmT0SZm4qoA56JvVhs/5g3A7RLolA85A1ASOTye65NMxASK6syfxGITMzvMxG9CvRkliWwlOm9AsSOcitzU1NzK7mjuBkQvHtLK7iLBiB5PhttJSGpB8I8vM6kDuiHeUjoVwMfYR4SRtUAw1veIZzOjRhSBzCoyKFjgH/3K7+BHzg+Cgw0eSW3AAAAAElFTkSuQmCC)
;
}
list .static-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* outline: none; bYO! inner rect on selection*/
}
list[type='text'][inlineeditable] input {
box-sizing: border-box;
margin: 0;
width: 100%;
}
list > :not([editing]) [displaymode='edit'] {
display: none;
}
list > [editing] [displaymode='static'] {
/* Don't use display:none or visibility:hidden because we need to keep an
* element focusable.
* We shrink only height. We don't shrink width to avoid to change the size
* of containing boxes. */
border-bottom: 0 !important;
border-top: 0 !important;
height: 0 !important;
width: auto;
margin-bottom: 0 !important;
margin-top: 0 !important;
overflow: hidden;
pointer-events: none;
}
list > [editing] input:invalid {
background-color: pink;
}
/* UI Controls */
/* LIST */ /* bYO! top level outline... */
.settings-list[has-element-focus]:after {
outline: 1px solid rgba(0, 128, 256, 0.5);
outline-offset: -1px;
content: "";
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
pointer-events: none;
z-index: 3;
}
/* chrome://settings/contentExceptions#... */
#content-settings-exceptions-area {
min-width: 540px;
width: 540px;
}
.exception-pattern {
-webkit-box-flex: 1;
-webkit-margin-end: 10px;
-moz-margin-end: 10px;
-webkit-margin-start: 14px;
-moz-margin-start: 14px;
width: 200px;
}
.exception-setting {
display: inline-block;
width: 120px;
}
select.exception-setting {
vertical-align: middle;
-webkit-padding-start: 3px;
width: calc(120px + 4px);
}
.overruled .exception-setting {
width: calc(120px - 24px);
}
.overruled .overruleable:focus {
outline:auto !important;
}
.overruled .overruleable {
text-decoration: line-through;
}
#exception-column-headers {
margin-left: 17px;
-webkit-margin-start: 17px;
display: -webkit-box;
margin-top: 10px; /* bYO! 6 lines on FF */
line-height: 16px;
}
#exception-column-headers > div {
font-weight: bold;
}
#exception-pattern-column {
-webkit-box-flex: 1;
}
.exception-value-column-header {
width: 145px;
}
#content-settings-exceptions-area list {
/*margin-bottom: 10px; bYO! keep list area consistent with scrolling */
margin-top: 6px;
}
/****** new ui compat... ******/
button:not(.custom-appearance) {
--google-blue-500: #528de0; /* #3b72ce #4285f4 #0a84ff */
font-weight: 500;
line-height: 100%; /* 154% */
padding: 8px 10px;
color: white !important;
border: 1px solid var(--google-blue-500);
border-radius: 3px;
box-shadow: none !important;
font-family: inherit;
font-size: inherit;
text-shadow: none;
}
button:enabled:not(.custom-appearance) {
background: var(--google-blue-500) !important;
}
button:enabled:not(.custom-appearance):focus {
background: rgb(58, 117, 215) !important;
}
select {
--md-arrow-width: 0.9em;
--md-arrow-offset: 0%;
--md-select-side-padding: 3px;
--md-select-width: 110px;
-webkit-appearance: none;
-moz-appearance: none;
/* -webkit-margin-end: calc(-1 * var(--md-select-side-padding)); */
/* -moz-margin-end: var(--md-select-side-padding); */
/* -webkit-padding-end: calc(var(--md-select-side-padding) + var(--md-arrow-offset) + var(--md-arrow-width) + 3px); */
-webkit-padding-start: var(--md-select-side-padding);
-moz-padding-start: calc(var(--md-select-side-padding) - 1px);
background: url(arrow_down.svg) calc(100% - var(--md-arrow-offset) - var(--md-select-side-padding)) center no-repeat !important;
background-image: url(arrow_down.svg) !important;
background-size: var(--md-arrow-width) !important;
border: none;
font-family: inherit;
font-size: inherit;
outline: none;
padding-bottom: 2px;
padding-top: 3px;
border-bottom: 1px solid rgb(224, 224, 224);
width: var(--md-select-width);
/* width: calc(var(--md-select-width, 200px) + 2 * var(--md-select-side-padding)); */
box-shadow: none !important;
text-shadow: none;
margin: 3px 0;
}
select:focus {
border-color: rgb(77, 144, 254);
}
select.exception-setting {
-webkit-padding-start: var(--md-select-side-padding);
width: calc(120px + var(--md-select-side-padding));
}
html > body {
height: 376px;
width: 500px;
margin: 16px 40px 14px 40px;
}
.overlay {
background-color: rgba(255, 255, 255,.5);
bottom: 0px;
padding-top: 30px;
}
.overlay .page {
max-height: 341.5px;
box-shadow: 0 0px 1px 0px rgba(0, 0, 0, 0.2), 0 1px 6px rgba(0,0,0,0.15);
}
body > div:first-child {
padding-bottom: 5px;
}
.action-area {
padding-top: .3em !important;
}
label.settings-label {
width: 80px;
margin: 7px 0px;
}
input.settings-checkbox ~ label.settings-label {
-webkit-margin-start: 3px;
-moz-margin-start: 3px;
}
select ~ button.exceptions-list-button{
-webkit-margin-start: auto;
-moz-margin-start: auto;
}
input.settings-checkbox {
display: -moz-box;
}
button:enabled:not(.custom-appearance):not(:focus):hover {
border: 1px solid rgba(0, 0, 0, 0.1); /* #3b72ce */
}
button:disabled:not(.custom-appearance) {
border-color: rgba(80, 80, 80, 0.1);
}
list input[type="text"] {
font-size: inherit;
font-family: inherit;
}
#perms-type {
-moz-padding-start: 3px;
height: 20px;
}
button::-moz-focus-inner {
border: 0;
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}

View File

@ -0,0 +1,124 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link href="options.css" rel="stylesheet" type="text/css" />
<script src="/options.js"></script>
<style type="text/css">
body {
font-family: "Segoe UI", Tahoma, sans-serif;
width: 560px;
height: 400px;
}
h1 {
font-size: 1.5em;
}
h2 {
margin: 1em 17px;
font-size: 1.3em;
}
h1, h2, h3 {
user-select: none;
font-weight: normal;
line-height: 1;
}
hr {
border-style: solid;
color: #dddddd;
border-top-width: 0px;
margin-top: 1em;
margin-bottom: 1.5em;
margin-inline-start: initial;
margin-inline-end: initial;
}
input.settings-checkbox {
margin: auto 1px;
-webkit-margin-start: 0px;
margin-inline-start: 0px;
position: static;
}
</style>
</head>
<body>
<div style="text-align: center; display: block;">
<span i18n-content="version">Version</span>
<span id="extension-version"></span>
</div>
<h1> <span i18n-content="settings" style="position: relative; top: -.7em">Settings</span></h1>
<h2 i18n-content="autoplayTabLabel">Autoplay</h2>
<div class="settings-row">
<label for="autoplay-default" class="settings-label" i18n-content="defaultMode">Default Mode:</label>
<select id="autoplay-default">
<option i18n-content="disableAutoplay" value="2">Disable Autoplay</option>
<option i18n-content="allowAutoplay" value="1">Allow Autoplay</option>
</select>
<button class="exceptions-list-button" contenttype="autoplay" i18n-content="manageExceptions">Exceptions...</button>
</div>
<hr />
<h2 i18n-content="flashTabLabel">Flash</h2>
<div class="settings-row">
<label class="settings-label" for="flash-default" i18n-content="defaultMode">Default Mode:</label>
<select id="flash-default">
<option i18n-content="allowFlash" value="1">Allow Flash</option>
<option i18n-content="disableFlash" value="2">Disable Flash</option>
</select>
<button class="exceptions-list-button" contenttype="flash" i18n-content="manageExceptions">Exceptions...</button>
</div>
<div class="overlay transparent" hidden>
<div id="content-settings-exceptions-area" class="page">
<button class="close-button raw-button custom-appearance"></button>
<h2 contenttype="autoplay" i18n-content="autoplayExceptions">Autoplay exceptions</h2>
<h2 contenttype="flash" i18n-content="flashExceptions" hidden>Flash exceptions</h2>
<div class="content-area" tabindex="-1">
<div id="exception-column-headers">
<div id="exception-pattern-column" i18n-content="exceptionHostnameHeader">
Hostname</div>
<div id="exception-behavior-column" class="exception-value-column-header" i18n-content="exceptionBehaviorHeader">
Behavior</div>
</div>
<div contenttype="autoplay">
<list id="autoplay-list" aria-activedescendant="listitem-27" class="settings-list" inlineeditable="" mode="normal" role="list" tabindex="-1">
<div class="spacer" style="height: 0px;"></div>
<div role="listitem" id="listitem" class="deletable-item" aria-posinset="1" aria-setsize="3"><div><div class="exception-pattern weakrtl"><div id="perms-host" class="static-text overruleable" displaymode="static" tabindex="-1"></div><input id="perms-host-input" type="text" displaymode="edit" tabindex="0"></div><span id="perms-type" class="exception-setting overruleable" displaymode="static" tabindex="-1"></span><select id="perms-type-select" tabindex="0" class="exception-setting" aria-labelledby="exception-behavior-column" displaymode="edit"><option value="1" i18n-content="exceptionAllow">Allow</option><option value="2" i18n-content="exceptionBlock">Block</option><option value="3" i18n-content="exceptionPrompt">Block (strict)</option><option value="8" i18n-content="exceptionSession">Session</option><option value="0" i18n-content="exceptionUndefined">Undefined</option></select></div><button class="raw-button row-delete-button custom-appearance" tabindex="-1" title="Delete this item"></button></div>
<div role="listitem" id="inputitem" selected="selected" lead="lead" class="deletable-item"><div><div class="exception-pattern weakrtl"><input id="perms-host-input" type="text" tabindex="0" placeholder="http://example.com"></div><select id="perms-type-select" tabindex="0" class="exception-setting" aria-labelledby="exception-behavior-column"><option value="1" i18n-content="exceptionAllow">Allow</option><option value="2" i18n-content="exceptionBlock">Block</option><option value="3" i18n-content="exceptionPrompt">Block (strict)</option><option value="8" i18n-content="exceptionSession">Session</option><option value="0" i18n-content="exceptionUndefined">Undefined</option></select></div><button class="raw-button row-delete-button custom-appearance" tabindex="-1" title="Delete this item" disabled=""></button></div>
<div class="spacer" style="height: 0px;"></div>
</list>
</div>
<div contenttype="flash" hidden>
<list id="flash-list" aria-activedescendant="listitem-27" class="settings-list" inlineeditable="" mode="normal" role="list" tabindex="-1">
<div class="spacer" style="height: 0px;"></div>
<div role="listitem" id="listitem" class="deletable-item" aria-posinset="1" aria-setsize="3"><div><div class="exception-pattern weakrtl"><div id="perms-host" class="static-text overruleable" displaymode="static" tabindex="-1"></div><input id="perms-host-input" type="text" displaymode="edit" tabindex="0"></div><span id="perms-type" class="exception-setting overruleable" displaymode="static" tabindex="-1"></span><select id="perms-type-select" tabindex="0" class="exception-setting" aria-labelledby="exception-behavior-column" displaymode="edit"><option value="1" i18n-content="exceptionAllow">Allow</option><option value="2" i18n-content="exceptionBlock">Block</option><option value="0" i18n-content="exceptionUndefined">Undefined</option></select></div><button class="raw-button row-delete-button custom-appearance" tabindex="-1" title="Delete this item"></button></div>
<div role="listitem" id="inputitem" selected="selected" lead="lead" class="deletable-item"><div><div class="exception-pattern weakrtl"><input id="perms-host-input" type="text" tabindex="0" placeholder="http://example.com"></div><select id="perms-type-select" tabindex="0" class="exception-setting" aria-labelledby="exception-behavior-column"><option value="1" i18n-content="exceptionAllow">Allow</option><option value="2" i18n-content="exceptionBlock">Block</option><option value="0" i18n-content="exceptionUndefined">Undefined</option></select></div><button class="raw-button row-delete-button custom-appearance" tabindex="-1" title="Delete this item" disabled=""></button></div>
<div class="spacer" style="height: 0px;"></div>
</list>
</div>
</div>
<div class="action-area" style="padding-top: .5em">
<div class="hbox stretch">
</div>
<button id="exceptions-clear" style="margin-left: 20px;" i18n-content="removeAll">Remove All</button>
<div class="action-area-right" style="-webkit-box-pack: end; display: -webkit-box;">
<div class="button-strip" reversed="" style="position:relative; top: -1.5em; right: 19px">
<button id="exceptions-apply" disabled i18n-content="apply">Apply</button>
<button id="exceptions-confirm" class="default-button" i18n-content="done">Done</button>
</div>
</div>
</div>
</div>
</div>
<hr />
<h2 i18n-content="advanced">Advanced</h2>
<div class="settings-row">
<input id="devtools" class="settings-checkbox" type="checkbox" />
<label class="settings-label" i18n-content="devtools">Devtools Panel</label>
</div>
</body>
</html>

View File

@ -0,0 +1,53 @@
body {
margin: 8px;
}
.popupmenu {
display: flex;
flex-direction: column;
color: #303942;
background: #f9fafb;
-webkit-user-select: none;
}
.popupmenu .popupitem {
cursor: pointer;
display: block;
white-space: nowrap;
}
.popupmenu .popupitem:hover {
background: rgb(235, 235, 235); /*#565a63;
color: white;*/
}
.popupmenu .popupitem.separator {
background-color: rgba(0,0,0,0.15);
height: 1px;
pointer-events: none;
margin: 2px 0;
}
.popupmenu .popupitem:not(.separator) {
padding: 7px 10px;
border-radius: 2px;
}
.popupmenu .popupitem[checked] {
background: rgb(80, 135, 90); /*#54975f;*/
color: white;
}
.popupmenu .popupitem[checked="2"], .popupmenu .popupitem[checked="3"] {
background: rgb(135, 80, 90); /*#54975f;*/
color: white;
}
.popupmenu.restricted #allow_site, .popupmenu.restricted #allow_session, .popupmenu.restricted #disable_site {
opacity: 0.5;
pointer-events: none;
}
.popupmenu #allow_all[checked] {
background: darkgray;
}

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link href="popup.css" rel="stylesheet" />
<script src="/popup.js"></script>
</head>
<body>
<div id="popupmenu" class="popupmenu">
<section>
<div id="disable_site" class="popupitem" i18n-content="disableSite" >Disable flash for %S</div>
<div id="allow_site" class="popupitem" i18n-content="allowSite" >Allow autoplay for %S</div>
<div id="allow_session" class="popupitem" i18n-content="allowSession" >Allow session autoplay for %S</div>
</section>
<div class="popupitem separator"></div>
<section>
<!-- <popup-item id="script">Edit script...</popup-item> -->
<div id="allow_all" class="popupitem" i18n-content="allowAll" >Allow everywhere</div>
<div id="settings" class="popupitem" i18n-content="settings" >Settings</div>
</section>
</div>
</body>
</html>

103
AutoplayStopper/utils.js Normal file
View File

@ -0,0 +1,103 @@
"use strict";
const i18n = new function(){
return {
process: function (doc) {
var elements = doc.querySelectorAll("[i18n-content]");
for (var i = 0; i < elements.length; ++i) {
var element = elements[i];
var message = chrome.i18n.getMessage(element.getAttribute("i18n-content"));
if (message) element.textContent = message;
else
console.warn("i18n: no message for " + element.getAttribute("i18n-content"));
}
}
};
};
const Storage = function Storage(keys, areaname){
var listeners = [];
var data = {};
var ready = new Promise(function (resolve, reject) {
chrome.storage[areaname].get(keys, function(items){
Object.assign(data, items);
if (!chrome.runtime.lastError) resolve();
})
});
chrome.storage.onChanged.addListener(function(changes, area) {
if (area == areaname) {
var updated = [];
for (var key of Object.keys(changes))
if (keys.indexOf(key) != -1) { data[key] = changes[key].newValue; updated.push(key); };
fireChanged(updated);
}
});
return {
get ready() { return ready; },
get data() { return data; },
addChangeListener: function(listener) { listeners.push(listener); return listener; },
removeChangeListener: function(listener) { var idx = listeners.indexOf(listener); if (idx != -1) listeners.splice(idx); },
commit: function commit(keys, callback) {
var items = {};
for (var key of keys) items[key] = data[key];
chrome.storage[areaname].set(items, function(){
if (callback) callback(chrome.runtime.lastError);
});
}
};
function fireChanged(updated) { for (var listener of listeners) listener(updated); };
};
const Permission = {
UNKNOWN_ACTION: 0,
ALLOW_ACTION: 1,
DENY_ACTION: 2,
PROMPT_ACTION: 3,
ACCESS_SESSION: 8
};
const Permissions = class Permissions {
constructor(data) {
function getData(type){ return data[Permissions.key(type)] || (data[Permissions.key(type)] = {}); };
function getDefault(type){ return data[Permissions.defaultKey(type)]; };
Object.assign(this, {
testPermission: function testPermission(type, uri) {
var origin, url = new window.URL(uri);
var hostTokens = url.host.split(".");
do {
var permission = getData(type)[origin = url.protocol + "//" + hostTokens.join(".")];
if (permission != undefined && permission != Permission.UNKNOWN_ACTION)
return Object.assign(Number(permission), {origin});
} while (hostTokens.shift());
return getDefault(type);
},
clear: function clear(type) { data[Permissions.key(type)] = {}; },
set: function set(type, url, perm) { getData(type)[new window.URL(url).origin] = perm; },
remove: function remove(type, url) { delete getData(type)[new window.URL(url).origin]; },
get: function get(type, url) { return getData(type)[new window.URL(url).origin]; },
setDefault: function setDefault(type, perm) { data[Permissions.defaultKey(type)] = perm; },
default: function _default(type) { return getDefault(type); },
entries: function entries(type) { return Object.entries(getData(type)); },
});
};
key(type) { return Permissions.key(type); };
defaultKey(type) { return Permissions.defaultKey(type); };
static key(type) { return `perms:${type}`; };
static defaultKey(type) { return `perms:${type}Default`; };
};
Object.assign(Permissions.prototype, Permission);
//</>