Skip to content

Instantly share code, notes, and snippets.

@natronium
Last active December 15, 2017 19:16
Show Gist options
  • Save natronium/7e4dbb8a9d378cc3d7df17a65443d32e to your computer and use it in GitHub Desktop.
Save natronium/7e4dbb8a9d378cc3d7df17a65443d32e to your computer and use it in GitHub Desktop.
pug.experience.mrrobot@shield.mozilla.org.xpi contents

This gist is the contents of pug.experience.mrrobot@shield.mozilla.org.xpi, extracted and then flattened using backslashes as "path separators" (gists don't support directories).

Generated with something that looked like this:

cd /scratchdir
unzip /path/to/pug.experience.mrrobot\@shield.mozilla.org.xpi
cd /flatgistdir
for f in $(find /scratchdir/* -type f); do
    cp "${f}" "./$(echo "${f}" | sed -e 's#/scratchdir/##' -e 's#/#\\#g')";
done

The extension itself was grabbed from a fresh profile, created with a fresh firefox install.

"use strict";
/* global Services */ // Cu.import
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "(startup|shutdown|install|uninstall)", "argsIgnorePattern": "^_" }]*/
const { utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LegacyExtensionsUtils",
"resource://gre/modules/LegacyExtensionsUtils.jsm");
const prefs = Services.prefs;
// our pref, set it with default false, if it's not there.
const PREFNAME = "extensions.pug.lookingglass";
if (!prefs.prefHasUserValue(PREFNAME)) {
prefs.setBoolPref(PREFNAME, false);
}
async function startup(addonData, _reason) {
// IFF the pref is true, startup
if (prefs.getBoolPref(PREFNAME, false)) {
addonData.webExtension.startup();
}
// set a watcher for that pref.
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrefBranch
prefs.addObserver(PREFNAME, (_aSubject, _aTopic, _aData) => {
const userOptedIn = prefs.getBoolPref(PREFNAME, false);
/* per https://dxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm#4387-4400
* only 'startup' is exposed, not 'shutdown'
*
* use this method:
* - https://searchfox.org/mozilla-central/source/browser/extensions/screenshots/bootstrap.js#168
*/
const realWebExtension = LegacyExtensionsUtils.getEmbeddedExtensionFor(addonData);
if (userOptedIn) realWebExtension.startup();
else realWebExtension.shutdown();
});
}
resource lookingglass .
content lookingglass-icons .
<?xml version="1.0" encoding="utf-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>pug.experience.mrrobot@shield.mozilla.org</em:id>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
<em:unpack>false</em:unpack>
<em:version>1.0.4</em:version>
<em:name>Looking Glass</em:name>
<em:description>MY REALITY IS JUST DIFFERENT THAN YOURS.
Looking Glass is a collaboration between Mozilla and the makers of Mr. Robot to provide a shared world experience. Are you a fan of Mr. Robot? If so, join the hunt for answers!
Participating in this shared world experience requires explicit user opt in. If you are not actively participating in the ARG (Augmented Reality Game) no modifications will be made to Firefox.
https:&#x2F;&#x2F;support.mozilla.org&#x2F;kb&#x2F;lookingglass
</em:description>
<em:creator>PUG Experience Group (Gregg Lind, Bianca Danforth, Kamyar Ardekani, Matt Grimes, Diana Livits, Jeffrey Kaufman and others) &lt;glind@mozilla.com&gt;</em:creator>
<em:homepageURL>https:&#x2F;&#x2F;support.mozilla.org&#x2F;kb&#x2F;lookingglass</em:homepageURL>
<em:iconURL>chrome://lookingglass-icons/content/icon.png</em:iconURL>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>57.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<em:targetApplication>
<Description>
<em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
<em:minVersion>57.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>
Manifest-Version: 1.0
Name: install.rdf
Digest-Algorithms: MD5 SHA1
MD5-Digest: 6kWUjsOkR4vMFDZO1UhXoA==
SHA1-Digest: By1c/PjnPLARKkRVm6pbk9a2WPg=
Name: chrome.manifest
Digest-Algorithms: MD5 SHA1
MD5-Digest: qz1mnvtsHDVldL//m6/7CA==
SHA1-Digest: JutsPFbIzQ7GhMXPFRTZKYC8T84=
Name: bootstrap.js
Digest-Algorithms: MD5 SHA1
MD5-Digest: ykYBHadbi1b97GQCuIPSsQ==
SHA1-Digest: P30+i9mpV8XoQkYWvHvYb2T1zgs=
Name: webextension/.eslintrc.json
Digest-Algorithms: MD5 SHA1
MD5-Digest: RwQmXGSA5QzTCjLjb8+plQ==
SHA1-Digest: 6hNm4Mp0TBMnasfN8SMKg5OOCzs=
Name: webextension/background.js
Digest-Algorithms: MD5 SHA1
MD5-Digest: 8dJdLz2Fk+4yIi9YyfwXWA==
SHA1-Digest: iXOg/8SeGEPVyvX6JZ+zFeGAIug=
Name: webextension/content-script.js
Digest-Algorithms: MD5 SHA1
MD5-Digest: 9bPbSd96ZsNcFLcOXpDh8g==
SHA1-Digest: 5Ow8jRok7M+HgZIn9hFt3+GFpcg=
Name: webextension/manifest.json
Digest-Algorithms: MD5 SHA1
MD5-Digest: 84tg1t3IkF0ybbKxufz5Fg==
SHA1-Digest: 0YJs+TaFlO+ICj38Hw/DrVbOJMU=
Name: webextension/test-page.html
Digest-Algorithms: MD5 SHA1
MD5-Digest: 0s1mQM7JHYjTrH/uJijn4g==
SHA1-Digest: 2sc1CuF3YPCfwCgt2pVus9dKIL4=
Signature-Version: 1.0
MD5-Digest-Manifest: 7GH0K6eT451Aiaoa3Fj/XQ==
SHA1-Digest-Manifest: gas1ZV1KEsGllDoM+7qXof5SPYE=
{
"env": {
"browser": true,
"es6": true,
"webextensions": true
},
"extends": [
"eslint:recommended"
],
"rules": {
"no-console": "warn"
}
}
"use strict";
/**
* background.js - persistent state and 'all tabs and sites' effects
*
* the webExtenion is only started up the user turns the pref on in bootstrap.js
*/
// constants used by particular effects
const WORDS =
`
puppet
one
zero
congo
rewind
71
fsociety
encrypt
decrypt
control
illusion
dark
army
evil
robot
disintegration
hack
society
white
rose
revolution
subroutine
backdoor
undo
society
corporation
economy
mask
system
truth
debt
cryptocurrency
kernel
panic
privacy
money
data
fuck
`.trim().split(/\s+/);
const XHEADERSITES = [
"https://www.red-wheelbarrow.com/forkids/*",
"https://www.whatismybrowser.com/detect/*",
"https://red-wheelbarrow-stage.apps.nbcuni.com/forkids/*"
];
const XHEADERNAME = 'x-1057';
const XHEADERVALUE = 'true'
/**
* Affect page views for all urls.
*
* In particular, the first time any word from wordArray
* is seen during a session, apply special styling to it.
*/
class PersistentPageModificationEffect {
/**
* wordArray: array of words to apply effect to
* CSS: string. Style for .donotdelete classname
*
* sets up:
* this.
* wordSet: Set() of words to 'affect'
* CSS
*/
constructor(wordArray, CSS) {
if (!CSS) CSS = `
.donotdelete {
transform: scaleY(-1);
/* scaleY does not work for inline elements */
display: inline-block;
}
.donotdelete-revert {
transform: scaleY(1);
transition: transform 1s ease-in;
}
/* after revert, revert the tooltip */
.donotdelete-revert .donotdelete-tooltip {
transform: scaleY(1) translateY(-50%);
transition: transform 1s ease-in;
}
.donotdelete-tooltip {
visibility: hidden !important;
display: inline-block;
position: absolute;
min-height: 50px;
max-width: 200px;
min-width: 200px;
padding: 5px;
text-align: center;
border-radius: 3px;
border: 1px solid #bebdbd;
box-shadow: var(--standard-box-shadow);
z-index: 50 !important;
/* Neutralizing styles */
font-style: normal !important;
transform: scaleY(-1) translateY(50%);
color: black !important;
text-shadow: none !important;
line-height: 16px !important;
font-family: sans-serif !important;
font-size: 12px !important;
font-weight: normal !important;
background-color: #ffffff !important;
letter-spacing: 0 !important;
text-transform: none !important;
}
.donotdelete-tooltip a {
color: blue !important;
text-decoration: underline !important;
font-weight: normal !important;
}
/* Neutralizes case where <a> have after pseudoelements like ">>" */
.donotdelete-tooltip a::after {
display: none;
}
/*
* Ensures hover effect is always on top; needed when two or more
* match words with hover effects occur directly next to one another
* in a given text node
*/
.donotdelete:hover {
z-index: 999 !important;
}
/* Show the tooltip when hovering */
.donotdelete:hover .donotdelete-tooltip {
visibility: visible !important;
}
/* Vertical centering */
[data-tooltip-position] {
top: 50%;
/*
* transform: translateY(-50%);
* (if we weren't already transforming this element)
*/
}
[data-tooltip-position="right"] {
left: 100%;
margin-left: 0;
}
[data-tooltip-position="left"] {
right: 100%;
margin-right: 0;
}`;
this.wordSet = new Set(wordArray);
this.insertCSSOnAllTabs().then(() => this.addListeners());
this.portFromCS = null;
this.APPLICABLE_PROTOCOLS = ["http:", "https:", "file:"];
this.CSS = {
code: CSS
};
}
addListeners() {
browser.runtime.onConnect.addListener((port) => this.connected(port));
}
/**
* msg: one of
* getList => returns this.wordSet (list of words)
* wordUsed => remove a word from this.wordSet
*/
connected(p) {
this.portFromCS = p;
this.portFromCS.onMessage.addListener((m) => {
switch (m.type) {
case "getList":
this.portFromCS.postMessage({ type: "getList", data: this.wordSet });
break;
case "wordUsed":
// remove word from the list
this.wordSet.delete(m.word);
break;
default:
throw new Error(`Message type not recognized: ${m.type}`);
}
});
}
/**
* Ensure that the CSS modification happens for all open and future tabs
*/
async insertCSSOnAllTabs() {
// When first loaded, add CSS for open tabs.
var gettingAllTabs = browser.tabs.query({});
gettingAllTabs.then(async (tabs) => {
for (let tab of tabs) {
if (this.protocolIsApplicable(tab.url)) {
await browser.tabs.insertCSS(tab.id, this.CSS);
this.portFromCS.postMessage({type: "cssLoaded"});
}
}
});
// Each time a tab is updated, add CSS for that tab.
browser.tabs.onUpdated.addListener(async (id, changeInfo, tab) => {
if (this.protocolIsApplicable(tab.url) && tab.status === "complete") {
await browser.tabs.insertCSS(id, this.CSS);
this.portFromCS.postMessage({type: "cssLoaded"});
}
});
}
/*
* Returns true only if the URL's protocol is in APPLICABLE_PROTOCOLS.
*/
protocolIsApplicable(url) {
var anchor = document.createElement('a');
anchor.href = url;
return this.APPLICABLE_PROTOCOLS.includes(anchor.protocol);
}
}
/**
* For certain domains, add a special header, using blocking requestHeaders API
*
* https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/onBeforeSendHeaders
*/
class AddHeaderForSpecialPage {
/**
* matchingUrls: array of url matches
* headerName: string single x-header to add to request
* value: value for the header value
*/
constructor(matchingUrls, headerName, headerValue) {
this.matchingUrls = matchingUrls;
this.headerName = headerName;
this.headerValue = headerValue;
this.registerHeaderListener();
}
listener(details) {
details.requestHeaders.push({name: this.headerName, value: this.headerValue});
return {requestHeaders: details.requestHeaders};
}
registerHeaderListener() {
browser.webRequest.onBeforeSendHeaders.addListener(
this.listener.bind(this),
{urls: this.matchingUrls},
["blocking", "requestHeaders"] // blocking = wait before sending
)
}
}
/**
* Once per addon startup, creates the effects
*/
async function runOnce() {
new PersistentPageModificationEffect(
WORDS
);
new AddHeaderForSpecialPage(
XHEADERSITES,
XHEADERNAME,
XHEADERVALUE
);
// when deciding whether to do the effect, check if allowed?
}
// actually start, once per run.
runOnce();
"use strict";
/*eslint no-cond-assign: "warn"*/
/**
* Port to communicate with `background.js`, and messaging machinery
*
* This method won't send message to background until it's actually listening,
* and eliminates race conditions around that.
*/
var myPort = browser.runtime.connect({name:"port-from-cs"});
myPort.onMessage.addListener(function(m) {
switch (m.type) {
case "cssLoaded":
myPort.postMessage({ type: "getList" });
break;
case "getList":
findAndReplace(m.data);
break;
default:
throw new Error(`Message type not recognized: ${m.type}`);
}
});
const SUPPORTURL = "https://support.mozilla.org/kb/lookingglass";
function findAndReplace(wordList) {
// the ones we actually find and substitute
let seen = new Set();
const combinedRegex = new RegExp('(' + Array.from(wordList).join('|') + ')', 'i');
wrapWith(
document.body,
{
wrapTag: 'span',
wrapClass: 'donotdelete',
re: combinedRegex,
matchCb: function matchCb (matchObj) {
const observed = matchObj[0].toLowerCase();
seen.add(observed); // for debugging
// Per #22, not doing this after all.
// tell the background script that word has been used.
//myPort.postMessage({ type: "wordUsed", word: observed });
}
}
);
document.querySelectorAll(".donotdelete").forEach((node) => {
const hoverEle = document.createElement("span");
hoverEle.classList.add("donotdelete-tooltip");
hoverEle.setAttribute("data-tooltip-position", "right");
node.appendChild(hoverEle);
// eslint-disable-next-line no-unsanitized/property
hoverEle.innerHTML = `
Can you trust your perceptions?<br>
You chose this... a reminder of<br>
the forces at work in your world.<br>
If you no longer wish to peer<br>
through the looking glass, you can<br/>
<a href="${SUPPORTURL}" target="_blank" rel="noopener noreferrer">
return to blissful ignorance</a>`;
});
document.querySelectorAll('.donotdelete').
forEach(function (node) {
const delayToRevert = (10*Math.random() + 1)*1000;
setTimeout(function () {
node.classList.add("donotdelete-revert");
setTimeout(()=>node.removeChild(node.lastChild),15000);
}, delayToRevert);
});
}
/**
* element: root of the dom you want to traverse
* config: all keys required.
* - wrapTag: string. name of tag to wrap with:
* - wrapClass: string, name the class.
* - re: a regex to use to decide what to replace
* - matchCb: (badly implemented) callback function of what to do with a match.
* - note, very incomplete.
* - arity:
* - m: the matchObj for re if exists.
*
* uses the magic of TreeWalker:
* - https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter
*
*/
function wrapWith (element, config) {
const {
wrapTag,
wrapClass,
re,
matchCb
} = config;
let nodes = document.createTreeWalker(
// starting element
element,
// NodeFilter.SHOW_TEXT: Only consider nodes that are text nodes (nodeType 3)
NodeFilter.SHOW_TEXT,
// optional: Accept node always. Same a 'no function' here.
{ acceptNode: function(node) {
// Logic to determine whether to accept, reject or skip node
// Only wrap words inside <p> elements
const tag = node.parentElement.tagName;
if (tag !== "P") {
return NodeFilter.FILTER_REJECT;
}
return NodeFilter.FILTER_ACCEPT;
}
},
null
);
let node;
// loop through nodes
while (node = nodes.nextNode()) {
var p = node.parentNode;
var text = node.nodeValue;
var m;
while(m = text.match(re)) {
// callback on every match
matchCb(m);
var front, mid, end;
front = text.slice(0,m.index); // might be empty ''
mid = m[0];
end = text.slice(m.index + mid.length); // might be empty ''
text = end;
// todo this line is dangerous
p.insertBefore(document.createTextNode(front), node);
var word = p.insertBefore(document.createElement(wrapTag), node);
word.appendChild(document.createTextNode(mid));
word.className = wrapClass;
checkForOverflow(word);
}
node.nodeValue = text;
}
}
// Naive check for overflow:hidden on parent/grandparent element of word
// to have it display properly when it would otherwise be hidden;
// does not catch all cases.
function checkForOverflow(word) {
const wordParent = word.parentElement;
const wordParentStyle = getComputedStyle(wordParent);
const wordGrandparent = wordParent.parentElement;
const wordGrandparentStyle = getComputedStyle(wordGrandparent);
if (wordParentStyle.overflow === "hidden") {
wordParent.style.overflow = "visible";
} else if (wordGrandparentStyle.overflow === "hidden") {
wordGrandparent.style.overflow = "visible";
}
}
{
"name": "pug-webextension",
"version": "1.0.4",
"manifest_version": 2,
"permissions": [
"<all_urls>",
"activeTab",
"tabs",
"webRequest",
"webRequestBlocking",
"https://www.red-wheelbarrow.com/forkids/*",
"https://www.whatismybrowser.com/detect/*",
"https://red-wheelbarrow-stage.apps.nbcuni.com/forkids/activitysheet/"
],
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"]
}
]
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test page</title>
</head>
<body>
<p>dark thing</p>
<p>light thing</p>
<div>another thing</div>
<div>data data <b>data</b></div>
<pre id='wanted'>Answer here</pre>
<pre id='seen'>Answer here</pre>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment