1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-22 22:57:58 +01:00

Add plugins.

This commit is contained in:
Kris Maglione
2015-12-20 03:49:29 -08:00
parent f85f3df612
commit 5bb2e31150
9 changed files with 3210 additions and 0 deletions

1310
plugins/aardvark.js Executable file

File diff suppressed because it is too large Load Diff

51
plugins/curl.js Executable file
View File

@@ -0,0 +1,51 @@
"use strict";
var INFO =
["plugin", { name: "curl",
version: "0.3",
href: "http://dactyl.sf.net/pentadactyl/plugins#curl-plugin",
summary: "Curl command-line generator",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" },
"Kris Maglione"],
["license", { href: "http://opensource.org/licenses/mit-license.php" },
"MIT"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"This plugin provides a means to generate a ", ["tt", {}, "curl(1)"], " ",
"command-line from the data in a given form."],
["item", {},
["tags", {}, ";C"],
["strut"],
["spec", {}, ";C"],
["description", {},
["p", {},
"Generates a curl command-line from the data in the selected form. ",
"The command includes the data from each form element, along with ",
"the current User-Agent string and the cookies for the current ",
"page."]]]];
hints.addMode('C', "Generate curl command for a form", function(elem) {
if (elem.form)
var { url, postData, elements } = DOM(elem).formData;
else
var url = elem.getAttribute("href");
if (!url || /^javascript:/.test(url))
return;
url = util.newURI(url, null,
elem.ownerDocument.documentURIObject).spec;
let { shellEscape } = util.closure;
dactyl.clipboardWrite(["curl"].concat(
[].concat(
[["--form-string", shellEscape(datum)] for (datum of (elements || []))],
postData != null && !elements.length ? [["-d", shellEscape("")]] : [],
[["-H", shellEscape("Cookie: " + elem.ownerDocument.cookie)],
["-A", shellEscape(navigator.userAgent)],
[shellEscape(url)]]
).map(function(e) e.join(" ")).join(" \\\n ")).join(" "), true);
});
/* vim:se sts=4 sw=4 et: */

468
plugins/flashblock.js Executable file
View File

@@ -0,0 +1,468 @@
"use strict";
var INFO =
["plugin", { name: "flashblock",
version: "1.3",
href: "http://dactyl.sf.net/pentadactyl/plugins#flashblock-plugin",
summary: "Flash Blocker",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" },
"Kris Maglione"],
["license", { href: "http://opensource.org/licenses/mit-license.php" },
"MIT"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"This plugin provides the same features as the ever popular FlashBlock ",
"Firefox add-on. Place holders are substituted for flash animations and ",
"embedded videos. When clicked, the original embedded content is ",
"restored. Additionally, this plugin provides options to control which ",
"sites can play animations without restrictions and triggers to toggle ",
"the playing of animations on the current page."],
["item", {},
["tags", {}, "'fb' 'flashblock'"],
["spec", {}, "'flashblock' 'fb'"],
["type", {}, "boolean"],
["default", {}, "true"],
["description", {},
["p", {},
"Controls the blocking of flash animations. When true, place ",
"holders are substituted for flash animations on untrusted sites."]]],
["item", {},
["tags", {}, "'fbw' 'fbwhitelist'"],
["spec", {}, "'fbwhitelist' 'fbw'"],
["type", {}, "sitelist"],
["default", {}, ""],
["description", {},
["p", {},
"Controls which sites may play flash animations without user ",
"intervention. See ", ["ex", {}, ":mk" + config.name.toLowerCase() + "rc"], "."]]],
["item", {},
["tags", {}, ":flashplay :flp"],
["strut"],
["spec", {}, ":flashplay"],
["description", {},
["p", {},
"Plays any blocked flash animations on the current page."]]],
["item", {},
["tags", {}, ":flashstop :fls"],
["strut"],
["spec", {}, ":flashstop"],
["description", {},
["p", {},
"Stops any currently playing flash animations on the current ",
"page."]]],
["item", {},
["tags", {}, ":flashtoggle :flt"],
["strut"],
["spec", {}, ":flashtoggle"],
["description", {},
["p", {},
"Toggles the playing of all animations on the current page. If ",
"any flash animations are currently blocked, all may begin ",
"playing. Otherwise, all animations are stopped."],
["example", {},
["ex", {}, ":map"], " -silent ",
["k", { name: "A-p", link: "false" }],
" ", ["ex", {}, ":flashtoggle"],
["k", { name: "CR" }]]]]];
group.options.add(["flashblock", "fb"],
"Enable blocking of flash animations",
"boolean", true,
{ setter: reload });
group.options.add(["fbwhitelist", "fbw"],
"Sites which may run flash animations without prompting",
"sitelist", "",
{
completer: context => completion.visibleHosts(context),
privateData: true,
setter: reload,
validator: () => true,
});
["Play", "Stop"].forEach(action => {
group.commands.add(["flash" + action, "fl" + action[0]].map(String.toLowerCase),
action + " all flash animations on the current page",
function () { postMessage(content, "flashblock" + action) },
{ argCount: "0" }, true);
});
group.commands.add(["flashtoggle", "flt"],
"Toggle playing of flash animations on the current page",
function () {
if (buffer.allFrames().some(w => DOM("pseudoembed", w.document).length))
commands.get("flashplay").action();
else
commands.get("flashstop").action();
},
{ argCount: "0" }, true);
group.mappings.add([modes.NORMAL], ["<Leader>fbwhitelist"],
"Add the current site to the flash whitelist",
function () { whitelist.op("+", whitelist.parse(content.location.hostname)) });
group.mappings.add([modes.NORMAL], ["<Leader>fbWhitelist"],
"Toggle the current site in the flash whitelist",
function () {
let host = content.location.hostname;
if (!removeHost(host))
whitelist.op("+", whitelist.parse(host));
});
var enabled = options.get("flashblock");
var whitelist = options.get("fbwhitelist");
function postMessage(content, message) {
buffer.allFrames(content).forEach(f => { f.postMessage(message, "*"); });
}
function reload(values) {
//for (let [,t] in tabs.browsers)
// t.contentWindow.postMessage("flashblockReload", "*");
postMessage(gBrowser.mCurrentBrowser.contentWindow, "flashblockReload");
return values;
}
function removeHost(host) {
let len = whitelist.value.length;
let uri = util.createURI(host);
whitelist.value = whitelist.value.filter(f => !f(uri));
return whitelist.value.length != len;
}
function onUnload() {
group.events.unlisten(null);
}
group.events.listen(window, "flashblockCheckLoad",
function checkLoadFlash(event) {
if(!enabled.value || whitelist.getKey(event.target.documentURIObject))
event.preventDefault();
event.stopPropagation();
}, true, true);
var data = {
bindings: "dactyl://data/text/xml," + encodeURIComponent('<?xml version="1.0"?>' +
String.raw`
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="flash">
<implementation>
<constructor>
<![CDATA[
var myDocument = XPCNativeWrapper(document);
var myWindow = XPCNativeWrapper(window);
function copyAttribs(to, from) {
Array.map(from.attributes, function(attrib) {
to.setAttribute(attrib.name, attrib.value);
});
}
function capitalize(str) { return str[0].toUpperCase() + str.substr(1) };
function Placeholder(embed) {
var self = this;
this.embed = embed;
if (!document.flashblockStyle) {
var head = document.getElementsByTagName("head")[0];
var node = document.createElement("style");
node.setAttribute("type", "text/css");
head.insertBefore(node, head.firstChild);
document.flashblockStyle = node.sheet;
}
document.flashblockIdx = (document.flashblockIdx || 0) + 1;
this.idx = document.flashblockIdx;
embed.setAttribute("flashblock", this.idx);
document.flashblockStyle.insertRule("pseudoembed[flashblock='" + this.idx + "'] {}", 0);
this.style = document.flashblockStyle.cssRules[0].style;
this.div = myDocument.createElement('pseudoembed');
this.div.addEventListener("click", function() { self.showEmbed(true) }, true);
this.div.flashblockEmbed = embed;
}
Placeholder.prototype = {
showEmbed: function(clicked) {
this.embed.clicked = clicked;
if (this.embed.parentNode)
return;
copyAttribs(this.embed, this.div);
this.div.parentNode.replaceChild(this.embed, this.div);
},
hideEmbed: function() {
let parent = this.embed.parentNode;
if (!parent)
return;
this.div.setAttribute("embedtype", this.embed.localName);
copyAttribs(this.div, this.embed);
['width', 'height'].forEach(function(dimen) {
this.style[dimen] = "";
if (this.embed[dimen])
if (/%$/.test(this.embed[dimen]))
this.style[dimen] = this.embed[dimen];
else
this.style[dimen] = parseInt(this.embed[dimen]) + "px";
}, this);
let style = myWindow.getComputedStyle(parent, "");
if (style.getPropertyValue("text-align") == "center") {
this.style.marginRight = "auto";
this.style.marginLeft = "auto";
}
parent.replaceChild(this.div, this.embed);
}
}
var parent = this.parentNode
var self = this;
if (!this.getAttribute("flashblock"))
this.setAttribute("flashblock", true);
if (this.placeholder || parent.placeholder)
return;
this.placeholder = new Placeholder(self);
function checkReplace(e) {
if (!e || e.data == "flashblockReload") {
if (self.clicked)
return;
let event = myDocument.createEvent("UIEvents");
event.initEvent("flashblockCheckLoad", true, true);
myDocument.dispatchEvent(event);
if (event.getPreventDefault())
self.placeholder.showEmbed();
else
self.placeholder.hideEmbed();
}
else if (e.data == "flashblockPlay")
self.placeholder.showEmbed(true);
else if (e.data == "flashblockStop")
self.placeholder.hideEmbed();
}
checkReplace();
myWindow.addEventListener("message", checkReplace, false);
// if(this.src == this.ownerDocument.location)
// myWindow.location = 'dactyl://data/application/xhtml+xml,' + encodeURIComponent('<?xml version="1.0" encoding="UTF-8"?>' +
// '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' +
// <html xmlns="http://www.w3.org/1999/xhtml">
// <head><title></title></head>
// <body>{new XML(parent.innerHTML)}</body>
// </html>);
]]>
</constructor>
</implementation>
</binding>
</bindings>
`),
flash: `data:image/png;base64,
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
`,
play: `data:image/png;base64,
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
AAMACHALg12qSjsAAAAASUVORK5CYII
`,
};
var CSS =
/*
* Flash Click to View by Ted Mielczarek (luser_mozilla@perilith.com)
* Original code by Jesse Ruderman (jruderman@hmc.edu)
* taken from http://www.squarefree.com/userstyles/xbl.html
*
* Change XBL binding for <object> tags, click to view flash
*/
String.literal`
pseudoembed {
display: inline-block;
min-width: 32px !important;
min-height: 32px !important;
border: 1px solid #dfdfdf;
cursor: pointer;
overflow: hidden;
-moz-box-sizing: border-box;
background: url("${data.play}") no-repeat center;
}
pseudoembed:hover {
background-image: url("${data.flash}");
}
video,
object[classid*=":D27CDB6E-AE6D-11cf-96B8-444553540000"],
object[codebase*="swflash.cab"],
object[data*=".swf"],
embed[type="application/x-shockwave-flash"],
embed[src*=".swf"],
object[type="application/x-shockwave-flash"],
object[src*=".swf"] {
-moz-binding: url("{bindings}") !important;
}
/// TODO: Could do better.
/// NoScript is incredibly annoying. The binding can't execute JS on
/// untrusted sites.
video:not([flashblock]),
object[classid*=":D27CDB6E-AE6D-11cf-96B8-444553540000"]:not([flashblock]),
object[codebase*="swflash.cab"]:not([flashblock]),
object[data*=".swf"]:not([flashblock]),
embed[type="application/x-shockwave-flash"]:not([flashblock]),
embed[src*=".swf"]:not([flashblock]),
object[type="application/x-shockwave-flash"]:not([flashblock]),
object[src*=".swf"]:not([flashblock]) {
display: none !important;
}
/// Java identifiers.
/// TODO: Make this work.
/// applet,
/// object[classid*=":8AD9C840-044E-11D1-B3E9-00805F499D93"],
/// object[classid^="clsid:CAFEEFAC-"],
/// object[classid^="java:"],
/// object[type="application/x-java-applet"],
/// embed[classid*=":8AD9C840-044E-11D1-B3E9-00805F499D93"],
/// embed[classid^="clsid:CAFEEFAC-"],
/// embed[classid^="java:"],
/// embed[type="application/x-java-applet"]
/// {
/// -moz-binding: url("{bindings}") !important;
/// }
`.replace(/\/\/\/.*/gm, "");
styles.system.add("flashblock", "*", CSS);
data = null;
CSS = null;
/* vim:se sts=4 sw=4 et: */

152
plugins/http-headers.js Executable file
View File

@@ -0,0 +1,152 @@
"use strict";
isGlobalModule = true;
var INFO =
["plugin", { name: "http-headers",
version: "0.7",
href: "http://dactyl.sf.net/pentadactyl/plugins#http-headers-plugin",
summary: "HTTP header info",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" },
"Kris Maglione"],
["license", { href: "http://opensource.org/licenses/mit-license.php" },
"MIT"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"Adds request and response headers to the <ex>:pageinfo</ex> ",
"command, with the keys ", ["em", {}, "h"], " and ", ["em", {}, "H"], " respectively. ",
"See also ", ["o", {}, "pageinfo"], "."],
["example", {}, ["ex", {}, ":pageinfo hH"]]];
var { Buffer } = require("buffer");
var Controller = Class("Controller", XPCOM(Ci.nsIController), {
init: function (command, data) {
this.command = command;
this.update(data);
},
get wrappedJSObject() { return this; },
supportsCommand: function (cmd) { return cmd === this.command; },
});
var HttpObserver = Class("HttpObserver",
XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference, Ci.nsIWebProgressListener]), {
init: function init() {
util.addObserver(this);
},
cleanup: function cleanup() {
this.observe.unregister();
},
extractHeaders: function extractHeaders(request, type) {
let major = {}, minor = {};
request.QueryInterface(Ci.nsIHttpChannelInternal)["get" + type + "Version"](major, minor);
let headers = [[type.toUpperCase(), "HTTP/" + major.value + "." + minor.value]];
request["visit" + type + "Headers"]({
visitHeader: function (header, value) {
headers.push([header, value]);
}
});
return headers;
},
getHeaders: function getHeaders(win, request) {
request.QueryInterface(Ci.nsIChannel);
let headers = overlay.getData(win.document, "headers", Object);
if ("response" in headers)
return;
if (win && /^https?$/.test(request.URI.scheme)) {
if (request instanceof Ci.nsIHttpChannel)
request.QueryInterface(Ci.nsIHttpChannel);
else {
request.QueryInterface(Ci.nsIMultiPartChannel);
request.baseChannel.QueryInterface(Ci.nsIHttpChannel);
}
headers.request = this.extractHeaders(request, "Request");
headers.request[0][1] = request.requestMethod + " " +
request.URI.path + " " + headers.request[0][1];
try {
headers.response = this.extractHeaders(request, "Response");
headers.response[0][1] += " " + request.responseStatus + " " +
request.responseStatusText;
}
catch (e) {}
let controller = this.getController(win);
if (controller)
win.controllers.removeController(controller);
win.controllers.appendController(Controller("dactyl-headers", { headers: headers, url: win.document.documentURI }));
}
},
observers: {
"http-on-examine-response": util.wrapCallback(function onExamineResponse(request, data) {
request.QueryInterface(Ci.nsIChannel).QueryInterface(Ci.nsIHttpChannel).QueryInterface(Ci.nsIRequest);
if (request.loadFlags & request.LOAD_DOCUMENT_URI) {
try {
var win = request.notificationCallbacks.getInterface(Ci.nsIDOMWindow);
}
catch (e) {
return;
}
this.getHeaders(win, request);
try {
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
}
catch (e) {}
}
})
},
onStateChange: util.wrapCallback(function(webProgress, request, stateFlags, status) {
if ((stateFlags & this.STATE_START) && (stateFlags & this.STATE_IS_DOCUMENT))
this.getHeaders(webProgress.DOMWindow, request);
else if ((stateFlags & this.STATE_STOP) && (stateFlags & this.STATE_IS_DOCUMENT)) {
this.getHeaders(webProgress.DOMWindow, request);
try {
webProgress.removeProgressListener(this);
} catch (e) {}
}
}),
getController: function getController(win) {
for (let i of util.range(0, win.controllers.getControllerCount())) {
let controller = win.controllers.getControllerAt(i);
if (controller.supportsCommand("dactyl-headers") && controller.wrappedJSObject instanceof Controller)
return controller.wrappedJSObject;
}
}
});
let observer = HttpObserver();
let onUnload = observer.closure.cleanup;
function* iterHeaders(buffer, type) {
let win = buffer.focusedFrame;
let store = win.document[overlay.id];
if (!store || !store.headers)
store = observer.getController(win);
if (store)
for (let [k, v] of values(store.headers[type] || []))
yield [k, v];
}
iter({ h: "Request", H: "Response" }).forEach(function ([key, name]) {
Buffer.addPageInfoSection(key, name + " Headers", function (verbose) {
if (verbose)
return iterHeaders(this, name.toLowerCase())
});
});
/* vim:se sts=4 sw=4 et: */

147
plugins/jscompletion.js Executable file
View File

@@ -0,0 +1,147 @@
"use strict";
var INFO =
["plugin", { name: "jscompletion",
version: "1.0.4",
href: "http://dactyl.sf.net/pentadactyl/plugins#jscompletion-plugin",
summary: "JavaScript completion enhancements",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" },
"Kris Maglione"],
["license", { href: "http://people.freebsd.org/~phk/" },
"BEER-WARE"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"This plugin provides advanced completion functions for ",
"DOM functions, eval, and some other special functions. ",
"For instance, ",
"", ["ex", {}, ':js content.document.getElementById("',
["k", { name: "Tab", link: "c_<Tab>" }], ], " ",
"should provide you with a list of all element IDs ",
"present on the current web page. Many other DOM ",
"methods are provided, along with their namespaced variants."]];
function evalXPath(xpath, doc, namespace) {
let res = doc.evaluate(xpath, doc,
function getNamespace(prefix) {
return {
html: "http://www.w3.org/1999/xhtml",
dactyl: NS,
ns: namespace
}[prefix];
},
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null
);
return function* () {
for (let i = 0; i < res.snapshotLength; i++)
yield res.snapshotItem(i);
}();
}
let NAMESPACES = [
["http://purl.org/atom/ns#", "Atom 0.3"],
["http://www.w3.org/2005/Atom", "Atom 1.0"],
[NS, "Dactyl"],
["http://www.w3.org/2005/Atom", "RSS"],
["http://www.w3.org/2000/svg", "SVG"],
["http://www.mozilla.org/xbl", "XBL"],
["http://www.w3.org/1999/xhtml", "XHTML 1.0"],
["http://www.w3.org/2002/06/xhtml2", "XHTML 2.0"],
["http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "XUL"]
];
function addCompleter(names, fn) {
for (let name of util.debrace(names))
javascript.completers[name] = fn;
}
function* uniq(iter) {
let seen = new RealSet()
for (let val of iter)
if (!seen.add(val))
yield val;
}
addCompleter("__lookup{Getter,Setter}__", function (context, func, obj, args) {
if (args.length == 1)
context.completions =
[[k, obj[func](k)] for (k of properties(obj))].concat(
[[k, obj[func](k)] for (k of properties(obj, true))]).filter(
([k, v]) => v);
});
addCompleter("eval", function (context, func, obj, args) {
if (args.length > 1)
return [];
if (!context.cache.js) {
context.cache.js = JavaScript();
context.cache.context = CompletionContext("");
}
let ctxt = context.cache.context;
context.keys = { text: "text", description: "description" };
ctxt.filter = context.filter;
context.cache.js.complete(ctxt);
context.advance(ctxt.offset);
context.completions = ctxt.allItems.items;
});
addCompleter("getOwnPropertyDescriptor", function (context, func, obj, args) {
context.anchored = false;
context.keys = { text: util.identity, description: () => "" };
if (args.length == 2)
return properties(args[0]);
});
addCompleter("getElementById", function (context, func, doc, args) {
context.anchored = false;
if (args.length == 1) {
context.keys = { text: e => e.getAttribute("id"), description: util.objectToString };
context.generate = () => evalXPath("//*[@id]", doc);
}
});
function addCompleterNS(names, fn) {
addCompleter(names + "{,NS}", function checkNS(context, func, obj, args) {
context.anchored = false;
context.keys = { text: util.identity, description: () => "" };
let isNS = /NS$/.test(func);
if (isNS && args.length == 1)
return NAMESPACES;
let prefix = isNS ? "ns:" : "";
return fn(context, func, obj, args, prefix, isNS && args.shift());
});
}
addCompleterNS("getElementsByClassName", function (context, func, doc, args, prefix, namespace) {
if (args.length == 1) {
let iter = evalXPath("//@" + prefix + "class", doc, namespace);
return array(e.value.split(" ") for (e of iter)).flatten().uniq().array;
}
});
addCompleterNS("{getElementsByTagName,createElement}", function (context, func, doc, args, prefix, namespace) {
if (args.length == 1) {
let iter = evalXPath("//" + prefix + "*", doc, namespace);
return uniq(e.localName.toLowerCase() for (e of iter));
}
});
addCompleterNS("getElementsByAttribute", function (context, func, doc, args, prefix, namespace) {
switch (args.length) {
case 1:
let iter = evalXPath("//@" + prefix + "*", doc, namespace);
return uniq(e.name for (e of iter));
case 2:
iter = evalXPath("//@" + prefix + args[0], doc, namespace);
return uniq(e.value for (e of iter));
}
});
addCompleterNS("{get,set,remove}Attribute", function (context, func, node, args, prefix, namespace) {
context.keys = { text: 0, description: 1 };
if (args.length == 1)
return [[a.localName, a.value]
for (a of array.iterValues(node.attributes))
if (!namespace || a.namespaceURI == namespace)];
});
/* vim:se sts=4 sw=4 et: */

483
plugins/noscript.js Executable file
View File

@@ -0,0 +1,483 @@
/*
* Copyright ©2010-2014 Kris Maglione <maglione.k at Gmail>
* Distributable under the terms of the MIT license.
*
* Documentation is at the tail of this file.
*/
"use strict";
if (!("noscriptOverlay" in window)) {
if (!userContext.noscriptIgnoreMissing)
dactyl.echoerr("This plugin requires the NoScript add-on.");
throw Finished();
}
/*
* this.globalJS ? !this.alwaysBlockUntrustedContent || !this.untrustedSites.matches(s)
* : this.jsPolicySites.matches(s) && !this.untrustedSites.matches(s) && !this.isForbiddenByHttpsStatus(s));
*/
function getSites() {
// This logic comes directly from NoScript. To my mind, it's insane.
const ns = services.noscript;
const global = options["script"];
const groups = { allowed: ns.jsPolicySites, temp: ns.tempSites, untrusted: ns.untrustedSites };
const show = RealSet(options["noscript-list"]);
const sites = window.noscriptOverlay.getSites();
const blockUntrusted = global && ns.alwaysBlockUntrustedContent;
let res = [];
for (let site of array.iterValues(Array.concat(sites.topSite, sites))) {
let ary = [];
let untrusted = groups.untrusted.matches(site);
let matchingSite = null;
if (!untrusted)
matchingSite = groups.allowed.matches(site) || blockUntrusted && site;
let enabled = Boolean(matchingSite);
if (site == sites.topSite && !ns.dom.getDocShellForWindow(content).allowJavascript)
enabled = false;
let hasPort = /:\d+$/.test(site);
if (enabled && !global || untrusted) {
if (!enabled || global)
matchingSite = untrusted;
if (hasPort && ns.ignorePorts)
if (site = groups.allowed.matches(site.replace(/:\d+$/, "")))
matchingSite = site;
ary.push(matchingSite);
}
else {
if ((!hasPort || ns.ignorePorts) && (show.has("full") || show.has("base"))) {
let domain = !ns.isForbiddenByHttpsStatus(site) && ns.getDomain(site);
if (domain && ns.isJSEnabled(domain) == enabled) {
ary = util.subdomains(domain);
if (!show.has("base") && ary.length > 1)
ary = ary.slice(1);
if (!show.has("full"))
ary = ary.slice(0, 1);
}
}
if (show.has("address") || ary.length == 0) {
ary.push(site);
if (hasPort && ns.ignorePorts) {
site = site.replace(/:\d+$/, "");
if (!groups.allowed.matches(site))
ary.push(site);
}
}
}
res = res.concat(ary);
}
let seen = RealSet();
return res.filter(function (h) {
let res = !seen.has(h);
seen.add(h);
return res;
});
}
function getObjects() {
let sites = noscriptOverlay.getSites();
let general = [], specific = [];
for (let group of values(sites.pluginExtras))
for (let obj of array.iterValues(group)) {
if (!obj.placeholder && (ns.isAllowedObject(obj.url, obj.mime) || obj.tag))
continue;
specific.push(obj.mime + "@" + obj.url);
general.push("*@" + obj.url);
general.push("*@" + obj.site);
}
sites = buffer.allFrames().map(f => f.location.host);
for (let filter of values(options["noscript-objects"])) {
let host = util.getHost(util.split(filter, /@/, 2)[1]);
if (sites.some(s => s == host))
specific.push(filter);
}
let seen = RealSet();
return specific.concat(general).filter(function (site) {
let res = !seen.has(site);
seen.add(site);
return res;
});
}
var onUnload = overlay.overlayObject(gBrowser, {
// Extend NoScript's bookmarklet handling hack to the command-line
// Modified from NoScript's own wrapper.
loadURIWithFlags: function loadURIWithFlags(url) {
let args = arguments;
let load = () => loadURIWithFlags.superapply(gBrowser, args);
if (!commandline.command || !util.isDactyl(Components.stack.caller))
return load();
try {
for (let [cmd, args] of commands.parseCommands(commandline.command))
var origURL = args.literalArg;
let isJS = url => /^(?:data|javascript):/i.test(url);
let allowJS = prefs.get("noscript.allowURLBarJS", true);
if (isJS(origURL) && allowJS) {
if (services.noscript.executeJSURL(origURL, load))
return;
}
else if (url != origURL && isJS(url)) {
if(services.noscript.handleBookmark(url, load))
return;
}
}
catch (e) {
util.reportError(e);
}
return load();
}
});
highlight.loadCSS(String.raw`
NoScriptAllowed color: green;
NoScriptBlocked color: #444; font-style: italic;
NoScriptTemp color: blue;
NoScriptUntrusted color: #c00; font-style: italic;
`);
let groupProto = {};
["temp", "jsPolicy", "untrusted"].forEach(function (group) {
memoize(groupProto, group,
function () {
return services.noscript[group + "Sites"].matches(this.site);
});
});
let groupDesc = {
NoScriptTemp: "Temporarily allowed",
NoScriptAllowed: "Allowed permanently",
NoScriptUntrusted: "Untrusted",
NoScriptBlocked: "Blocked"
};
function splitContext(context, list) {
for (let [name, title, filter] of values(list)) {
let ctxt = context.split(name);
ctxt.title = [title];
ctxt.filters.push(filter);
}
}
completion.noscriptObjects = function (context) {
let whitelist = options.get("noscript-objects").set;
context = context.fork();
context.compare = CompletionContext.Sort.unsorted;
context.generate = getObjects;
context.keys = {
text: util.identity,
description: key => whitelist.has(key) ? "Allowed" : "Forbidden"
};
splitContext(context, getObjects, [
["forbidden", "Forbidden objects", item => !whitelist.has(item.item)],
["allowed", "Allowed objects", item => whitelist.has(item.item)]]);
};
completion.noscriptSites = function (context) {
context.compare = CompletionContext.Sort.unsorted;
context.generate = getSites;
context.keys = {
text: util.identity,
description: site => groupDesc[this.highlight] +
(this.groups.untrusted && this.highlight != "NoScriptUntrusted" ? " (untrusted)" : ""),
highlight: function (site) {
return this.groups.temp ? "NoScriptTemp" :
this.groups.jsPolicy ? "NoScriptAllowed" :
this.groups.untrusted ? "NoScriptUntrusted" :
"NoScriptBlocked";
},
groups: site => ({ site: site, __proto__: groupProto })
};
splitContext(context, [
["normal", "Active sites", item => item.groups.jsPolicy || !item.groups.untrusted],
["untrusted", "Untrusted sites", item => !item.groups.jsPolicy && item.groups.untrusted]]);
context.maxItems = 100;
}
services.add("noscript", "@maone.net/noscript-service;1");
var PrefBase = "noscript.";
var Pref = Struct("text", "pref", "description");
let prefs = {
forbid: [
["bookmarklet", "forbidBookmarklets", "Forbid bookmarklets"],
["collapse", "collapseObject", "Collapse forbidden objects"],
["flash", "forbidFlash", "Block Adobe® Flash® animations"],
["fonts", "forbidFonts", "Forbid remote font loading"],
["frame", "forbidFrames", "Block foreign <frame> elements"],
["iframe", "forbidIFrames", "Block foreign <iframe> elements"],
["java", "forbidJava", "Block Java™ applets"],
["media", "forbidMedia", "Block <audio> and <video> elements"],
["placeholder", "showPlaceholder", "Replace forbidden objects with a placeholder"],
["plugins", "forbidPlugins", "Forbid other plugins"],
["refresh", "forbidMetaRefresh", "Block <meta> page directions"],
["silverlight", "forbidSilverlight", "Block Microsoft® Silverlight™ objects"],
["trusted", "contentBlocker", "Block media and plugins even on trusted sites"],
["webbug", "blockNSWB", "Block “web bug” tracking images"],
["xslt", "forbidXSLT", "Forbid XSLT stylesheets"]
],
list: [
["address", "showAddress", "Show the full address (http://www.google.com)"],
["base", "showBaseDomain", "Show the base domain (google.com)"],
["full", "showDomain", "Show the full domain (www.google.com)"]
]
};
for (let [k, v] of iter(prefs))
prefs[k] = array(v).map(v => [v[0], Pref.fromArray(v.map(UTF8))]).toObject();
function getPref(pref) { return modules.prefs.get(PrefBase + pref); }
function setPref(pref, val) { return modules.prefs.set(PrefBase + pref, val); }
prefs.complete = group => context => {
context.keys = { text: "text", description: "description" };
context.completions = values(prefs[group]);
};
prefs.get = function (group) { return [p.text for (p of values(this[group])) if (getPref(p.pref))]; };
prefs.set = function (group, val) {
for (let p of values(this[group]))
setPref(p.pref, val.indexOf(p.text) >= 0);
return val;
}
prefs.descs = function prefDescs(group) {
return ["dl", {},
template.map(values(this[group]), pref =>
[["dt", {}, pref.text], ["dd", {}, pref.description]])];
};
function groupParams(group) {
return {
getter: () => prefs.get(group),
completer: prefs.complete(group),
setter: val => prefs.set(group, val),
initialValue: true,
persist: false
};
}
group.options.add(["noscript-forbid", "nsf"],
"The set of permissions forbidden to untrusted sites",
"stringlist", "",
groupParams("forbid"));
group.options.add(["noscript-list", "nsl"],
"The set of domains to show in the menu and completion list",
"stringlist", "",
groupParams("list"));
group.options.add(["script"],
"Whether NoScript is enabled",
"boolean", false,
{
getter: () => services.noscript.jsEnabled,
setter: (val) => services.noscript.jsEnabled = val,
initialValue: true,
persist: false
});
[
{
names: ["noscript-sites", "nss"],
description: "The list of sites allowed to execute scripts",
action: (add, sites) => sites.length && noscriptOverlay.safeAllow(sites, add, false, -1),
completer: (context) => completion.noscriptSites(context),
has: (val) => hasOwnProperty(services.noscript.jsPolicySites.sitesMap, val) &&
!hasOwnProperty(services.noscript.tempSites.sitesMap, val),
get set() {
return RealSet(k for (k in services.noscript.jsPolicySites.sitesMap))
.difference(RealSet(k for (k in services.noscript.tempSites.sitesMap)))
}
}, {
names: ["noscript-tempsites", "nst"],
description: "The list of sites temporarily allowed to execute scripts",
action: (add, sites) => sites.length && noscriptOverlay.safeAllow(sites, add, true, -1),
completer: (context) => completion.noscriptSites(context),
get set() { return RealSet(k for (k in services.noscript.tempSites.sitesMap)) },
}, {
names: ["noscript-untrusted", "nsu"],
description: "The list of untrusted sites",
action: (add, sites) => sites.length && services.noscript.setUntrusted(sites, add),
completer: (context) => completion.noscriptSites(context),
get set() { return RealSet(k for (k in services.noscript.untrustedSites.sitesMap)) },
}, {
names: ["noscript-objects", "nso"],
description: "The list of allowed objects",
get set() { return RealSet(array.flatten(
[Array.concat(v).map(function (v) { return v + "@" + this; }, k)
for ([k, v] of iter(services.noscript.objectWhitelist))])) },
action: function (add, patterns) {
for (let pattern of values(patterns)) {
let [mime, site] = util.split(pattern, /@/, 2);
if (add)
services.noscript.allowObject(site, mime);
else {
let list = services.noscript.objectWhitelist[site];
if (list) {
if (list == "*") {
delete services.noscript.objectWhitelist[site];
services.noscript.objectWhitelistLen--;
}
else {
let types = list.filter(type => type != mime);
services.noscript.objectWhitelistLen -= list.length - types.length;
services.noscript.objectWhitelist[site] = types;
if (!types.length)
delete services.noscript.objectWhitelist[site];
}
}
}
}
if (add)
services.noscript.reloadAllowedObjects(config.browser.selectedBrowser);
},
completer: context => completion.noscriptObjects(context)
}
].forEach(function (params) {
group.options.add(params.names, params.description,
"stringlist", "",
{
completer: function (context) {
context.anchored = false;
if (params.completer)
params.completer(context)
},
domains: params.domains || (values => values),
has: params.has || bind("has", params.set),
initialValue: true,
getter: params.getter || (() => Array.from(params.set)),
setter: function (values) {
let newset = RealSet(values);
let current = params.set;
let value = this.value;
params.action(true, values.filter(site => !current.has(site)))
params.action(false, value.filter(site => !newset.has(site)));
return this.value;
},
persist: false,
privateData: true,
validator: params.validator || (() => true),
})
});
var INFO =
["plugin", { name: "noscript",
version: "0.9",
href: "http://dactyl.sf.net/pentadactyl/plugins#noscript-plugin",
summary: "NoScript integration",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" }, "Kris Maglione"],
["license", { href: "http://opensource.org/licenses/mit-license.php" }, "MIT"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"This plugin provides tight integration with the NoScript add-on. ",
"In addition to commands and options to control the behavior of ",
"NoScript, this plugin also provides integration with both the ",
config.appName, " and ", config.host, " sanitization systems sorely ",
"lacking in the add-on itself. Namely, when data for a domain is ",
"purged, all of its associated NoScript permissions are purged as ",
"well, and temporary permissions are purged along with session ",
"data."],
["note", {},
"As most options provided by this script directly alter NoScript ",
"preferences, which are persistent, their values are automatically ",
"preserved across restarts."],
["item", {},
["tags", {}, "'script' 'noscript'"],
["strut", {}],
["spec", {}, "'script'"],
["type", {}, "boolean"],
["default", {}, "noscript"],
["description", {},
["p", {},
"When on, all sites are allowed to execute scripts and ",
"load plugins. When off, only specifically allowed sites ",
"may do so."]]],
["item", {},
["tags", {}, "'nsf' 'noscript-forbid'"],
["spec", {}, "'noscript-forbid'"],
["type", {}, "stringlist"],
["default", {}, ""],
["description", {},
["p", {},
"The set of permissions forbidden to untrusted sites."],
prefs.descs("forbid"),
["p", {},
"See also ", ["o", {}, "noscript-objects"], "."]]],
["item", {},
["tags", {}, "'nsl' 'noscript-list'"],
["spec", {}, "'noscript-list'"],
["type", {}, "stringlist"],
["default", {}, ""],
["description", {},
["p", {},
"The set of items to show in the main completion list and ",
"NoScript menu."],
prefs.descs("list")]],
["item", {},
["tags", {}, "'nso' 'noscript-objects'"],
["spec", {}, "'noscript-objects'"],
["type", {}, "stringlist"],
["default", {}, ""],
["description", {},
["p", {},
"The list of objects which allowed to display. See also ",
["o", {}, "noscript-forbid"], "."],
["example", {},
["ex", {}, ":map ", ["k", { name: "A-c", link: "false" }]], " ",
["ex", {}, ":set nso!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]],
["item", {},
["tags", {}, "'nss' 'noscript-sites'"],
["spec", {}, "'noscript-sites'"],
["type", {}, "stringlist"],
["default", {}, ""],
["description", {},
["p", {},
"The list of sites which are permanently allowed to execute ",
"scripts."],
["example", {},
["ex", {}, ":map ", ["k", { name: "A-s", link: "false" }]], " ",
["ex", {}, ":set nss!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]],
["item", {},
["tags", {}, "'nst' 'noscript-tempsites'"],
["spec", {}, "'noscript-tempsites'"],
["type", {}, "stringlist"],
["default", {}, ""],
["description", {},
["p", {},
"The list of sites which are temporarily allowed to execute ",
"scripts. The value is not preserved across application ",
"restarts."],
["example", {},
["ex", {}, ":map ", ["k", { name: "A-S-s", link: "false" }]], " ",
["ex", {}, ":set nst!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]],
["item", {},
["tags", {}, "'nsu' 'noscript-untrusted'"],
["spec", {}, "'noscript-untrusted'"],
["type", {}, "stringlist"],
["default", {}, ""],
["description", {},
["p", {},
"The list of untrusted sites which are not allowed to activate, ",
"nor are listed in the main completion lists or NoScript menu."],
["example", {},
["ex", {}, ":map ", ["k", { name: "A-C-s", link: "false" }]], " ",
["ex", {}, ":set nsu!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]]];
/* vim:se sts=4 sw=4 et: */

177
plugins/tab-options.js Executable file
View File

@@ -0,0 +1,177 @@
/*
* "THE BEER-WARE LICENSE" (Revision 42):
* <maglions.k at Gmail> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Kris Maglione
* ---------------------------------------------------------------------------
* <phk@FreeBSD.ORG> wrote this license. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ---------------------------------------------------------------------------
*
* Documentation is at the tail of this file.
*/
"use strict";
let groupId = 0;
let onUnload = overlay.overlayObject(gBrowser, {
addTab: util.wrapCallback(function addTab(uri, params, charset, postData, ownerTab) {
if (!isObject(params) || params instanceof Ci.nsIURI)
params = { referrerURI: params, ownerTab: ownerTab };
let currentTab = tabs.getTab();
let tab = addTab.superapply(this, arguments);
if (!params.ownerTab && (params.referrerURI || params.relatedToCurrent))
params.ownerTab = currentTab;
if (params.ownerTab)
tab.dactylOwner = Cu.getWeakReference(params.ownerTab);
tab.dactylGroup = browser.groupId++;
if (params.ownerTab && (params.referrerURI || params.relatedToCurrent)) {
if (params.ownerTab.dactylGroup == null)
params.ownerTab.dactylGroup = tab.dactylGroup;
tab.dactylGroup = params.ownerTab.dactylGroup;
}
// This is a hack to deal with session restore.
if (uri === "about:blank" && params.skipAnimation && Object.keys(params).length == 1)
return tab;
let source = params.fromExternal ? "external" :
params.referrerURI ? "link"
: "orphan";
let location = options["tabopen"][source];
if (uri == null || location == null)
return tab;
let visible = tabs.visibleTabs;
let index = visible.indexOf(params.ownerTab || currentTab);
if (/left$/.test(location))
;
else if (/right$/.test(location))
index++;
else if ("start" == location)
index = 0;
else if ("end" == location)
index = visible.length;
if ("groupleft" == location)
while (index > 0 && visible[index].dactylGroup && visible[index].dactylGroup == currentTab.dactylGroup)
index--;
else if ("groupright" == location)
while (index < visible.length && visible[index].dactylGroup && visible[index].dactylGroup == currentTab.dactylGroup)
index++;
config.browser.moveTabTo(tab, tabs.allTabs.concat(undefined).indexOf(visible[index]));
return tab;
}),
removeTab: util.wrapCallback(function removeTab(tab) {
if (tab == tabs.getTab()) {
let tabList = tabs.visibleTabs;
let idx = tabList.indexOf(tab);
for (let val of values(options["tabclose"])) {
if (val == "opener" && tab.dactylOwner && tabs.allTabs.indexOf(tab.dactylOwner.get()) >= 0)
tabs.select(tab.dactylOwner.get());
else if (val == "previous" && tabs.alternate)
tabs.select(tabs.alternate);
else if (val == "left" && idx > 0)
tabs.select(idx - 1);
else if (val == "right" && idx < tabList.length - 1)
tabs.select(idx + 1);
else
continue;
break;
}
}
return removeTab.superapply(this, arguments);
}),
});
group.options.add(["tabclose", "tc"],
"Tab closure options, in order of precedence",
"stringlist", "left,opener,previous,right",
{
completer: context => [
["left", "Select the tab to the left when closing"],
["opener", UTF8("Select the tabs opener, if available")],
["previous", "Select the previously selected tab"],
["right", "Select the tab to the right when closing"]
]
});
group.options.add(["tabopen", "to"],
"Placement options for new tabs",
"stringmap", "link:right,orphan:groupright,external:end",
{
completer: function (context, extra) {
if (extra.value == null)
return [
["external", "Tabs opened from an external application"],
["link", "Tabs opened by clicking links and the like"],
["orphan", "Tabs opened by any other means"]
].filter(e => !hasOwnProperty(extra.values, e[0]));
return [
["end", "Open new tabs at the end of the tab bar"],
["groupleft", "Open tabs to the left of the current group"],
["groupright", "Open tabs to the right of the current group"],
["left", "Open new tabs to the left of the current tab"],
["right", "Open new tabs to the right of the current tab"],
["start", "Open new tabs at the start of the tab bar"]
]
}
});
var INFO =
["plugin", { name: "tab-options",
version: "0.3",
href: "http://dactyl.sf.net/pentadactyl/plugins#tab-options-plugin",
summary: "Tab options",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" }, "Kris Maglione"],
["license", { href: "http://people.freebsd.org/~phk/" }, "BEER-WARE"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"Adds extended tab options, including relative placement of new",
"tabs and more sensible focus changes after tab closure."],
["item", {},
["tags", {}, "'tc' 'tabclose'"],
["spec", {}, "'tabclose' 'tc'"],
["type", {}, "stringlist"],
["default", {}, options.get("tabclose").stringDefaultValue],
["description", {},
["p", {},
"Tab closure options, in order of precedence. The ",
"first item for which a valid tab exists is used."],
["dl", {},
template.map(options.get("tabclose").completer(),
([k, v]) =>
[["dt", {}, k], ["dd", {}, v]])],
["note", {},
"This option does not affect the default mappings for ",
["k", {}, "d"], "and ", ["k", {}, "D"],
", which behave as documented."]]],
["item", {},
["tags", {}, "'to' 'tabopen'"],
["spec", {}, "'tabopen' 'to'"],
["type", {}, "stringmap"],
["default", {}, options.get("tabopen").stringDefaultValue],
["description", {},
["p", {},
"New tab placement options. The keys in the ",
["t", {}, "stringmap"],
"refer to the ways the tab was opened, while the values define ",
"where such tabs are placed. The following keys are applicable:"],
["dl", {},
template.map(options.get("tabopen")
.completer(null, { values: {} }),
([k, v]) =>
[["dt", {}, k], ["dd", {}, v]])],
["p", {}, "As are the following values:"],
["dl", {},
template.map(options.get("tabopen")
.completer(null, { value: "" }),
([k, v]) =>
[["dt", {}, k], ["dd", {}, v]])]]]
];
/* vim:se sts=4 sw=4 et: */

213
plugins/useragent.js Executable file
View File

@@ -0,0 +1,213 @@
"use strict";
var INFO =
["plugin", { name: "useragent",
version: "0.3",
href: "http://dactyl.sf.net/pentadactyl/plugins#useragent-plugin",
summary: "User Agent Switcher",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" },
"Kris Maglione"],
["license", { href: "http://opensource.org/licenses/mit-license.php" },
"MIT"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"Ths plugin allows you to switch the browser's reported user-agent to a ",
"number of preset values."],
["item", {},
["tags", {}, ":ua :useragent"],
["spec", {}, ":useragent ", ["oa", {}, "name"], " ", ["oa", {}, "useragent"]],
["description", {},
["p", {},
"With zero or one arguments, list the currently defined ",
"user-agent values."],
["p", {},
"With two arguments, defines a new user-agent for use in the ",
["o", {}, "useragent"], " option. When ", ["o", {}, "useragent"], " is set to ",
"", ["oa", {}, "name"], ", the ", ["tt", {}, "User-Agent"], " value sent to web ",
"servers, and the value returned by ",
["tt", {}, "navigator.userAgent"], " will be ", ["oa", {}, "useragent"], ". ",
"Additionally, the following options are available:"],
["dl", {},
["dt", {}, "-appcodename"], ["dd", {}, "The value of ", ["tt", {}, "navigator.appCodeName"]],
["dt", {}, "-appname"], ["dd", {}, "The value of ", ["tt", {}, "navigator.appName"]],
["dt", {}, "-appversion"], ["dd", {}, "The value of ", ["tt", {}, "navigator.appVersion"]],
["dt", {}, "-platform"], ["dd", {}, "The value of ", ["tt", {}, "navigator.platform"]],
["dt", {}, "-vendor"], ["dd", {}, "The value of ", ["tt", {}, "navigator.vendor"]],
["dt", {}, "-vendorsub"], ["dd", {}, "The value of ", ["tt", {}, "navigator.vendorsub"]]]]],
["item", {},
["tags", {}, ":deluseragent :delua"],
["spec", {}, ":deluseragent ", ["a", {}, "name"]],
["description", {},
["p", {},
"Deletes a useragent created by ", ["ex", {}, ":useragent"], "."]]],
["item", {},
["tags", {}, "'useragent' 'ua'"],
["spec", {}, "'useragent' 'ua'"],
["description", {},
["p", {},
"Changes the User-Agent string sent to the web server and ",
"returned by ", ["tt", {}, "navigator.userAgent"], ". If the value is the ",
"name of a user-agent defined by ", ["ex", {}, ":useragent"], ", or one of ",
"the predefined values, then the defined value is used. ",
"Otherwise, the value itself is used."]]]];
let UserAgent, useragents;
let init = function init_() {
init = function () {};
UserAgent = Struct("name", "useragent", "appname", "appcodename",
"appversion", "platform", "vendor", "vendorsub", "userset");
UserAgent.prototype.__defineGetter__("options", function () {
return opts.slice(1).map(opt => [opt.name, this[opt.name]])
.filter(opt => opt[1]);
});
useragents = array([
// From User Agent Switcher 0.7.2
["ie-6", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
"Mozilla", "Microsoft Internet Explorer", "4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
"Win32"],
["ie-7", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Mozilla", "Microsoft Internet Explorer", "4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Win32"],
["ie-8", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)",
"Mozilla", "Microsoft Internet Explorer", "4.0 (compatible; MSIE 8.0; Windows NT 6.1)",
"Win32"],
["bot-googlebot-2.1", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"],
["bot-msnbot-1.1", "msnbot/1.1 (+http://search.msn.com/msnbot.htm)"],
["bot-yahoo", "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)"],
["iphone-3", "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16",
"Mozilla", "Netscape", "5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16",
"iPhone", "Apple Computer, Inc.", ""]
]).map(ua => [ua[0], UserAgent.fromArray(ua)]).toObject();
};
let Opt = Struct("name", "description", "pref", "names");
Opt.defaultValue("names", function () { return ["-" + this.name]; });
let opts = [
["useragent", "The value of navigator.userAgent", "general.useragent.override"],
["appcodename", "The value of navigator.appCodeName", "general.useragent.appName"],
["appname", "The value of navigator.appName", "general.appname.override"],
["appversion", "The value of navigator.appVersion", "general.appversion.override"],
["platform", "The value of navigator.platform", "general.platform.override"],
["vendor", "The value of navigator.vendor", "general.useragent.vendor"],
["vendorsub", "The value of navigator.vendorsub", "general.useragent.vendorSub"]
].map(Opt.fromArray, Opt);
group.options.add(["useragent", "ua"],
"The current browser user-agent",
"string", "default",
{
initValue: function () {},
completer: function (context, args) {
init();
context.title = ["Name", "User-Agent"];
context.keys = { text: "name", description: "useragent" };
context.completions = array(values(useragents)).concat(
[{ name: "default", useragent: navigator.userAgent }]);
},
setter: function (value) {
init();
let ua = useragents[value] ||
(value == "default" ? UserAgent("default")
: UserAgent("", value));
for (let opt of values(opts)) {
if (ua[opt.name])
prefs.safeSet(opt.pref, ua[opt.name], "See the 'useragent' option");
else
prefs.safeReset(opt.pref, "See the 'useragent' option");
}
return value;
},
validator: () => true,
});
group.commands.add(["useragent", "ua"],
"Define a new useragent.",
function (args) {
init();
if (args.length < 2)
commandline.commandOutput(template.tabular(["Name", "User-Agent"], ["padding-right: 1em; min-width: 8em;", "white-space: normal;"],
[[ua.name,
["",
ua.useragent,
!ua.options.length ? "" :
["span", { highlight: "URLExtra" },
" (",
template.map(ua.options, (o) =>
[["span", { highlight: "Key Normal" }, o[0]],
"=",
["span", { highlight: "String" }, o[1]]],
"\u00a0"),
")"]]
]
for (ua of values(useragents))
if (!args[0] || ua.name.indexOf(args[0]) >= 0)]));
else {
dactyl.assert(args.bang || !Set.has(useragents, args[0]),
"Useragent " + JSON.stringify(args[0]) + " already exists");
useragents[args[0]] = UserAgent.fromArray(
args.concat(opts.slice(1).map(
(opt) => args[opt.names[0]])));
useragents[args[0]].userset = true;
}
}, {
bang: true,
completer: function (context, args) {
init();
if (args.completeArg == 1)
context.completions = [[navigator.userAgent, "Default"]].concat(
[[v.useragent, k] for ([k, v] of iter(useragents))]);
},
literal: 1,
options: opts.slice(1).map((opt) => ({
names: opt.names,
description: opt.description,
completer: (context, args) =>
array(values(useragents)).map((ua) => ua[opt.name])
.compact().uniq()
.map((val) => [val, ""]).array,
type: CommandOption.STRING
})),
serialize: function () {
init();
return [
{
command: this.name,
arguments: [ua.name],
bang: true,
literalArg: ua.useragent,
options: array(
[opt.names[0], ua[opt.name]]
for (opt of values(opts.slice(1)))
if (ua[opt.name] != null)
).toObject()
}
for (ua of values(useragents)) if (ua.userset)
]
}
}, true);
group.commands.add(["deluseragent", "delua"],
"Deletes a useragent.",
function (args) {
init();
dactyl.assert(Set.has(useragents, args[0]), "Invalid argument");
if (options["useragent"] == args[0])
options["useragent"] = "default";
delete useragents[args[0]];
}, {
argCount: "1"
}, true);
/* vim:se sts=4 sw=4 et: */

209
plugins/xpcom.js Executable file
View File

@@ -0,0 +1,209 @@
"use strict";
var INFO =
["plugin", { name: "xpcom",
version: "0.4",
href: "http://dactyl.sf.net/pentadactyl/plugins#xpcom-plugin",
summary: "XPCOM development",
xmlns: "dactyl" },
["author", { email: "maglione.k@gmail.com" },
"Kris Maglione"],
["license", { href: "http://opensource.org/licenses/mit-license.php" },
"MIT"],
["project", { name: "Pentadactyl", "min-version": "1.0" }],
["p", {},
"This plugin aids in the development of XPCOM-related code, and ",
"in the exploration of extant XPCOM interfaces, classes, and ",
"instances. All of the functions herein are exported to the ",
"<em>userContext</em> and are thus available from the ",
"<ex>:javascript</ex> command. Each of these functions provides ",
"JavaScript completion for its arguments."],
["item", {},
["tags", {}, "xpwrapper"],
["spec", {}, "xpwrapper(<a>instance</a>, <oa>interface</oa>)"],
["spec", {}, "xpwrapper(<a>string</a>)"],
["description", {},
["p", {},
"This function is the core of the plugin. It wraps XPCOM ",
"objects so that their properties are more easily ",
"accessible. When ", ["a", {}, "instance"], " alone is given, the ",
"result contains one property for each interface that ",
["a", {}, "instance"], " implements. Each of those properties, in ",
"turn, returns ", ["a", {}, "instance"], " wrapped in a call to ",
["code", {}, "xpwrapper(", ["a", {}, "instance"], ", ", ["a", {}, "interface"], "),"],
"which contains only the properties of ", ["a", {}, "instance"], " ",
"specified in ", ["a", {}, "interface"], ". Additionally, the ",
"one-argument form contains the properties ", ["em", {}, "all"], "" ,
"and ", ["em", {}, "wrappedJSObject"], ", the former of which ",
"returns an object that implements all interfaces ",
"provided by the instance, and the latter of which, when ",
"applicable, is the raw JavaScript object that backs the ",
"XPCOM instance."],
["p", {},
"When ", ["a", {}, "string"], " is provided rather than an XPCOM ",
"instance, the returned object contains all of the ",
"properties specified by the interface with the given ",
"name, each with an ", ["hl", { key: "Object" }, "undefined"], " value."]]],
["item", {},
["tags", {}, "xpclasses"],
["spec", {}, "xpclasses(", ["a", {}, "class"], ")"],
["spec", {}, "xpclasses(", ["a", {}, "string"], ")"],
["description", {},
["p", {},
"When given an XPCOM instance as its first argument, ",
"the result is exactly the same as the one argument form ",
"of ", ["em", {}, "xpwrapper"], ". When given a string, returns the ",
"", ["em", {}, "xpwrapper"], " for an instance of the provided ",
"XPCOM contract ID."]]],
["item", {},
["tags", {}, "xpproviders"],
["strut"],
["spec", {}, "xpproviders"],
["description", {},
["p", {},
"Presents, for each installed interface, a property for ",
"each class that provides that interface. The properties ",
"on both levels are lazily instantiated, so iterating ",
"over the values of either level is not a good idea."],
["example", {},
["ex", {}, ':js xpproviders.nsILocalFile["',
["k", { name: "Tab", link: "c_<Tab>" }]]]]],
["item", {},
["tags", {}, "xpservices"],
["spec", {}, "xpservices(", ["a", {}, "class"], ")"],
["spec", {}, "xpservices[", ["a", {}, "class"], "]"],
["description", {},
["p", {},
"An object containing an ", ["t", {}, "xpwrapper"], " wrapped service for ",
"each contract ID in ", ["em", {}, "Components.classes"], "."]]]];
function Completer(obj) {
return [(context) => {
context.anchored = false;
return Object.keys(obj).map(k => [k, k]);
}];
}
userContext.xpwrapper = function xpwrapper(obj, iface) {
let res = {};
if (arguments.length == 2) {
try {
let shim = XPCOMShim([iface]);
iter.forEach(properties(shim), function (prop) {
res.__defineGetter__(prop, function () {
let res = obj.QueryInterface(Ci[iface])[prop];
if (callable(res)) {
let fn = (...args) => res.apply(obj, args);
fn.toString = () => res.toString();
fn.toSource = () => res.toSource();
return fn;
}
return res;
})
});
}
catch (e if e === Cr.NS_ERROR_NO_INTERFACE) {
res = null
}
}
else if (isString(obj))
return xpwrapper({}, obj);
else {
for (let iface in Ci)
if (Ci[iface] instanceof Ci.nsIJSIID)
try {
obj.QueryInterface(Ci[iface]);
memoize(res, iface, iface => xpwrapper(obj, iface));
}
catch (e) {};
memoize(res, "all", function (iface) {
try {
for (let iface of Object.keys(Ci))
obj instanceof Ci[iface];
}
catch (e) {}
return obj;
});
if ("wrappedJSObject" in obj)
memoize(res, "wrappedJSObject", () => obj.wrappedJSObject);
}
return res;
}
memoize(userContext, "xpclasses", function () {
function xpclasses(cls) {
if (typeof cls == "string")
cls = Cc[cls].createInstance();
return userContext.xpwrapper(cls);
}
Object.keys(Cc).forEach(function (k) {
xpclasses.__defineGetter__(k, () => xpclasses(k));
});
JavaScript.setCompleter([xpclasses], Completer(Cc));
return xpclasses;
});
memoize(userContext, "xpinterfaces", function () {
function xpinterfaces(inst) {
if (typeof inst == "string")
inst = Cc[inst].createInstance();
inst = inst.QueryInterface(Ci.nsIInterfaceRequestor);
let res = {};
for (let iface in Ci)
if (Ci[iface] instanceof Ci.nsIJSIID)
try {
inst.getInterface(Ci[iface]);
memoize(res, iface, iface => userContext.xpwrapper(inst.getInterface(Ci[iface])));
}
catch (e) {}
return res;
}
return xpinterfaces;
});
memoize(userContext, "xpservices", function () {
function xpservices(cls) {
if (typeof cls == "string")
cls = Cc[cls].getService();
return userContext.xpwrapper(cls);
}
Object.keys(Cc).forEach(function (k) {
xpservices.__defineGetter__(k, () => xpservices(k));
});
JavaScript.setCompleter([xpservices], Completer(Cc));
return xpservices;
});
JavaScript.setCompleter([userContext.xpwrapper], Completer(Ci));
memoize(userContext, "xpproviders", function () {
function xpproviders(iface) {
iface = Ci[iface];
let res = {};
for (let cls in Cc)
try {
if (Cc[cls].getService() instanceof iface)
memoize(res, cls, cls =>
userContext.xpwrapper(Cc[cls].getService(), iface));
}
catch (e) {}
return res;
}
for (let iface in Ci)
memoize(xpproviders, iface, xpproviders);
JavaScript.setCompleter([xpproviders], Completer(Ci));
return xpproviders;
});
/* vim:se sts=4 sw=4 et: */