зеркало из https://github.com/mozilla/janus-addon.git
Initial commit
This commit is contained in:
Коммит
22be6a1983
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
XPI=janus.xpi
|
||||
|
||||
# Replace this value to push to different release channels.
|
||||
# Nightly = org.mozilla.fennec
|
||||
# Aurora = org.mozilla.fennec_aurora
|
||||
# Beta = org.mozilla.firefox_beta
|
||||
# Release = org.mozilla.firefox
|
||||
ANDROID_APP_ID=org.mozilla.fennec
|
||||
|
||||
cfx xpi --templatedir template
|
||||
|
||||
# Push the add-on to your device to test
|
||||
adb push "$XPI" /sdcard/"$XPI" && \
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.DEFAULT \
|
||||
-d file:///mnt/sdcard/"$XPI" \
|
||||
-n $ANDROID_APP_ID/.App && \
|
||||
echo Pushed $XPI to $ANDROID_APP_ID
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 13 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 203 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 10 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 135 KiB |
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link href="panel-style.css" type="text/css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.usage-container {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-container">
|
||||
<input type="checkbox" id="enabled-checkbox">
|
||||
<label for="enabled-checkbox">Enable Janus Proxy</label><br>
|
||||
|
||||
<table class="usage-container">
|
||||
<tr><td>Traffic In:</td><td class="bold" id="bytes-ingress">0 B</td></tr>
|
||||
<tr><td>Traffic Out:</td><td class="bold" id="bytes-egress">0 B</td></tr>
|
||||
<tr><td>Non-proxy Traffic:</td><td class="bold" id="bytes-unknown">0 B</td></tr>
|
||||
<tr><td>Bandwidth Savings:</td><td class="bold" id="reduction-percentage">0%</td></tr>
|
||||
<tr rowspan="2"><td><button type="button" id="reset-button">Reset</button></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="panel.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
addon.port.on('usage', function(usage) {
|
||||
console.log("updating usages");
|
||||
document.getElementById('bytes-ingress').innerHTML = usage.totalIngress;
|
||||
document.getElementById('bytes-egress').innerHTML = usage.totalEgress;
|
||||
document.getElementById('bytes-unknown').innerHTML = usage.totalUnknown;
|
||||
document.getElementById('reduction-percentage').innerHTML = usage.reductionPercentage;
|
||||
});
|
||||
|
||||
var enabledCheckbox = document.getElementById('enabled-checkbox');
|
||||
addon.port.on('enabledChanged', function(enabled) {
|
||||
enabledCheckbox.checked = enabled;
|
||||
});
|
||||
|
||||
enabledCheckbox.addEventListener("click", function() {
|
||||
console.log("sending enabledChanged");
|
||||
addon.port.emit("enabledChanged", enabledCheckbox.checked);
|
||||
});
|
||||
|
||||
document.getElementById('reset-button').addEventListener('click', function() {
|
||||
addon.port.emit("reset");
|
||||
});
|
Двоичный файл не отображается.
|
@ -0,0 +1,138 @@
|
|||
const self = require("sdk/self");
|
||||
const { Cc, Ci, Cu, components } = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {TextEncoder, TextDecoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
|
||||
const SAVE_TIMEOUT_MS = "10000";
|
||||
|
||||
// Ripped off from DownloadUtils.js
|
||||
function convertByteUnits(aBytes) {
|
||||
let unitIndex = 0;
|
||||
let units = ["bytes", "kilobyte", "megabyte", "gigabyte"];
|
||||
|
||||
// Convert to next unit if it needs 4 digits (after rounding), but only if
|
||||
// we know the name of the next unit
|
||||
while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
|
||||
aBytes /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
// Get rid of insignificant bits by truncating to 1 or 0 decimal points
|
||||
// 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
|
||||
// added in bug 462064: (unitIndex != 0) makes sure that no decimal digit for bytes appears when aBytes < 100
|
||||
aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) && (unitIndex != 0) ? 1 : 0);
|
||||
|
||||
return aBytes + " " + units[unitIndex];
|
||||
}
|
||||
|
||||
var ByteTracker = {
|
||||
init: function() {
|
||||
this.totalIngress = 0;
|
||||
this.totalEgress = 0;
|
||||
this.totalUnknown = 0;
|
||||
this.listening = false;
|
||||
|
||||
this.loadAsync();
|
||||
|
||||
this.distributor = Cc['@mozilla.org/network/http-activity-distributor;1']
|
||||
.getService(Ci.nsIHttpActivityDistributor);
|
||||
},
|
||||
|
||||
start: function() {
|
||||
if (this.listening) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.distributor.addObserver(this);
|
||||
this.listening = true;
|
||||
|
||||
this.timer = components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(components.interfaces.nsITimer);
|
||||
this.timer.initWithCallback(this, SAVE_TIMEOUT_MS, this.timer.TYPE_REPEATING_SLACK);
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (!this.listening) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.saveAsync();
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
|
||||
try {
|
||||
this.distributor.removeObserver(this);
|
||||
this.listening = false;
|
||||
} catch(e) {}
|
||||
},
|
||||
|
||||
notify: function(timer) {
|
||||
this.saveAsync();
|
||||
},
|
||||
|
||||
getStorageFile: function() {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, "janus_addon_bytetracker.json");
|
||||
},
|
||||
|
||||
saveAsync: function() {
|
||||
let obj = {
|
||||
totalIngress: this.totalIngress,
|
||||
totalEgress: this.totalEgress,
|
||||
totalUnknown: this.totalUnknown
|
||||
};
|
||||
|
||||
let encoder = new TextEncoder();
|
||||
OS.File.writeAtomic(this.getStorageFile(), encoder.encode(JSON.stringify(obj)));
|
||||
},
|
||||
|
||||
loadAsync: function() {
|
||||
let decoder = new TextDecoder();
|
||||
let promise = OS.File.read(this.getStorageFile());
|
||||
let that = this;
|
||||
promise = promise.then(
|
||||
function onSuccess(array) {
|
||||
let obj = JSON.parse(decoder.decode(array));
|
||||
|
||||
that.totalIngress += obj.totalIngress;
|
||||
that.totalEgress += obj.totalEgress;
|
||||
that.totalUnknown += obj.totalUnknown;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.totalIngress = this.totalEgress = this.totalUnknown = 0;
|
||||
},
|
||||
|
||||
getUsages: function() {
|
||||
return {
|
||||
totalIngress: convertByteUnits(this.totalIngress),
|
||||
totalEgress: convertByteUnits(this.totalEgress),
|
||||
totalUnknown: convertByteUnits(this.totalUnknown),
|
||||
reductionPercentage: Math.round((((this.totalIngress + this.totalUnknown) - (this.totalEgress + this.totalUnknown)) /
|
||||
((this.totalIngress + this.totalUnknown) || 1)) * 100) + "%"
|
||||
};
|
||||
},
|
||||
|
||||
observeActivity: function(channel, type, subtype, timestamp, extraSizeData, extraStringData) {
|
||||
if (type === this.distributor.ACTIVITY_TYPE_HTTP_TRANSACTION &&
|
||||
subtype === this.distributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE) {
|
||||
try {
|
||||
var httpChannel = channel.QueryInterface(components.interfaces.nsIHttpChannel)
|
||||
this.totalIngress += parseInt(httpChannel.getResponseHeader('x-original-content-length'));
|
||||
this.totalEgress += extraSizeData;
|
||||
} catch(e) {
|
||||
// No x-original-content-length header for whatever reason, so
|
||||
// we don't know the original size. Count it as equal on both
|
||||
// sides, but keep track of how much of that stuff we get.
|
||||
this.totalUnknown += extraSizeData;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ByteTracker.init();
|
||||
|
||||
exports.ByteTracker = ByteTracker;
|
|
@ -0,0 +1,67 @@
|
|||
const self = require("sdk/self");
|
||||
const { Cc, Ci, Cu, components } = require("chrome");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
const { PREFS } = require("prefs");
|
||||
const { ByteTracker } = require("byteTracker");
|
||||
|
||||
var { ToggleButton } = require("sdk/ui/button/toggle");
|
||||
var panels = require("sdk/panel");
|
||||
|
||||
var gToggleButton, gPanel, gUpdateUsageTimer;
|
||||
|
||||
exports.onPrefChanged = function(name, value) {
|
||||
if (name === PREFS.ENABLED) {
|
||||
gToggleButton.icon = value ? "./janus-small.png" : "./janus-disabled-small.png";
|
||||
gPanel.port.emit("enabledChanged", value);
|
||||
}
|
||||
};
|
||||
|
||||
gPanel = panels.Panel({
|
||||
width: 250,
|
||||
height: 150,
|
||||
contentURL: self.data.url("panel.html"),
|
||||
onHide: function() {
|
||||
gToggleButton.state('window', { checked: false });
|
||||
|
||||
if (gUpdateUsageTimer) {
|
||||
gUpdateUsageTimer.cancel();
|
||||
gUpdateUsageTimer = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gPanel.port.on("enabledChanged", function(enabled) {
|
||||
Preferences.set(PREFS.ENABLED, enabled);
|
||||
});
|
||||
|
||||
gPanel.port.on("reset", function() {
|
||||
ByteTracker.reset();
|
||||
gPanel.port.emit('usage', ByteTracker.getUsages());
|
||||
});
|
||||
|
||||
gUpdateUsageTimer = null;
|
||||
var timerObserver = {
|
||||
notify: function() {
|
||||
gPanel.port.emit('usage', ByteTracker.getUsages());
|
||||
}
|
||||
}
|
||||
|
||||
gToggleButton = ToggleButton({
|
||||
id: "janus-enable-button",
|
||||
label: "Janus",
|
||||
icon: "./janus-small.png",
|
||||
onChange: function(state) {
|
||||
if (state.checked) {
|
||||
gPanel.port.emit('usage', ByteTracker.getUsages());
|
||||
|
||||
gPanel.show({
|
||||
position: gToggleButton
|
||||
});
|
||||
|
||||
gUpdateUsageTimer = components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(components.interfaces.nsITimer);
|
||||
gUpdateUsageTimer.initWithCallback(timerObserver, 1000, gUpdateUsageTimer.TYPE_REPEATING_SLACK);
|
||||
}
|
||||
}
|
||||
});
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,148 @@
|
|||
const self = require("sdk/self");
|
||||
const { Cc, Ci, Cu, components } = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
const { PREFS } = require("prefs");
|
||||
const { ByteTracker } = require("byteTracker");
|
||||
|
||||
const DEFAULT_PROXY_URL = "http://janus.allizom.org";
|
||||
const PROXY_TYPE = 2;
|
||||
|
||||
var gUI = null;
|
||||
|
||||
function isFennec() {
|
||||
return (Services.appinfo.ID == "{aa3c5121-dab2-40e2-81ca-7ea25febc110}");
|
||||
}
|
||||
|
||||
var ProxyAddon = {
|
||||
|
||||
rebuildHeader: function() {
|
||||
this.header = "";
|
||||
|
||||
var features = [
|
||||
{ pref: PREFS.ADBLOCK_ENABLED, option: 'adblock' },
|
||||
{ pref: PREFS.GIF2VIDEO_ENABLED, option: 'gif2video' }
|
||||
];
|
||||
|
||||
features.forEach((feature) => {
|
||||
this.header += (Preferences.get(feature.pref, false) ? "+" +
|
||||
feature.option : "-" + feature.option) + " ";
|
||||
});
|
||||
},
|
||||
|
||||
applyPrefChanges: function(name) {
|
||||
var value = Preferences.get(name);
|
||||
|
||||
if (name === PREFS.ENABLED) {
|
||||
//updateUI(value);
|
||||
this.enabled = value;
|
||||
|
||||
if (value) {
|
||||
Preferences.set(PREFS.PROXY_AUTOCONFIG_URL, Preferences.get(PREFS.PAC_URL));
|
||||
Preferences.set(PREFS.PROXY_TYPE, PROXY_TYPE);
|
||||
Services.obs.addObserver(ProxyAddon.observe, "http-on-modify-request", false);
|
||||
ByteTracker.start();
|
||||
} else {
|
||||
Preferences.reset(PREFS.PROXY_AUTOCONFIG_URL);
|
||||
Preferences.reset(PREFS.PROXY_TYPE);
|
||||
ByteTracker.stop();
|
||||
try {
|
||||
Services.obs.removeObserver(ProxyAddon.observe, "http-on-modify-request");
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
this.rebuildHeader();
|
||||
} else if (name === PREFS.PAC_URL) {
|
||||
Preferences.set(PREFS.PAC_URL, value);
|
||||
} else if (name === PREFS.ADBLOCK_ENABLED ||
|
||||
name === PREFS.GIF2VIDEO_ENABLED) {
|
||||
this.rebuildHeader();
|
||||
}
|
||||
|
||||
if (gUI.onPrefChanged) {
|
||||
gUI.onPrefChanged(name, value);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic === "nsPref:changed") {
|
||||
this.applyPrefChanges(data);
|
||||
} else if (topic === "http-on-modify-request") {
|
||||
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
channel.setRequestHeader("X-Janus-Options", ProxyAddon.header, false);
|
||||
}
|
||||
},
|
||||
|
||||
observeAddon: function(doc, topic, id) {
|
||||
if (id !== self.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
function updateUsage() {
|
||||
let bytesLabel = doc.getElementById("bytes-label");
|
||||
let reductionLabel = doc.getElementById("reduction-label");
|
||||
let usage = ByteTracker.getUsages();
|
||||
|
||||
bytesLabel.innerHTML = usage.totalEgress + " / " +
|
||||
usage.totalIngress + " (" + usage.totalUnknown +
|
||||
" not proxied)";
|
||||
|
||||
reductionLabel.innerHTML = "reduced by " + usage.reductionPercentage;
|
||||
}
|
||||
|
||||
let resetButton = doc.getElementById("reset-button");
|
||||
resetButton.innerHTML = "Reset";
|
||||
resetButton.addEventListener('click', function() {
|
||||
ByteTracker.reset();
|
||||
updateUsage();
|
||||
});
|
||||
|
||||
updateUsage();
|
||||
}
|
||||
}
|
||||
|
||||
const OBSERVE_PREFS = [PREFS.ENABLED, PREFS.PAC_URL,
|
||||
PREFS.ADBLOCK_ENABLED, PREFS.GIF2VIDEO_ENABLED];
|
||||
|
||||
require("sdk/system/unload").when(function unload(reason) {
|
||||
if (reason == 'shutdown') {
|
||||
return;
|
||||
}
|
||||
|
||||
OBSERVE_PREFS.forEach(function(pref) {
|
||||
Preferences.ignore(pref, ProxyAddon);
|
||||
});
|
||||
|
||||
Services.obs.removeObserver(ProxyAddon.observeAddon, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
|
||||
|
||||
// Put proxy prefs back to defaults
|
||||
Preferences.reset(PREFS.PROXY_AUTOCONFIG_URL);
|
||||
Preferences.reset(PREFS.PROXY_TYPE);
|
||||
|
||||
if (gUI) {
|
||||
gUI.shutdown(reason);
|
||||
}
|
||||
});
|
||||
|
||||
if (self.loadReason == 'install') {
|
||||
Preferences.set(PREFS.ENABLED, true);
|
||||
Preferences.set(PREFS.PAC_URL, DEFAULT_PROXY_URL);
|
||||
}
|
||||
|
||||
OBSERVE_PREFS.forEach(function(pref) {
|
||||
Preferences.observe(pref, ProxyAddon);
|
||||
});
|
||||
|
||||
Services.obs.addObserver(ProxyAddon.observeAddon, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
|
||||
|
||||
if (isFennec()) {
|
||||
gUI = require("fennec");
|
||||
} else {
|
||||
gUI = require("desktop");
|
||||
}
|
||||
|
||||
ProxyAddon.applyPrefChanges(PREFS.ENABLED);
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
exports.PREFS = {
|
||||
ENABLED: "extensions.janus.enabled",
|
||||
PAC_URL: "extensions.janus.pac_url",
|
||||
ADBLOCK_ENABLED: "extensions.janus.adblock.enabled",
|
||||
GIF2VIDEO_ENABLED: "extensions.janus.gif2video.enabled",
|
||||
|
||||
PROXY_AUTOCONFIG_URL: "network.proxy.autoconfig_url",
|
||||
PROXY_TYPE: "network.proxy.type",
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "janus",
|
||||
"title": "Janus Proxy Configurator",
|
||||
"id": "janus@mozilla.org",
|
||||
"description": "Configure your browser to use the Janus SPDY/HTTP2 proxy",
|
||||
"author": "James Willcox <jwillcox@mozilla.com>",
|
||||
"icon": "data/janus.png",
|
||||
"license": "MPL 2.0",
|
||||
"version": "1.2"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
exec cfx run --templatedir template
|
|
@ -0,0 +1,11 @@
|
|||
[App]
|
||||
Vendor=Varma
|
||||
Name=Test App
|
||||
Version=1.0
|
||||
BuildID=20060101
|
||||
Copyright=Copyright (c) 2009 Atul Varma
|
||||
ID=xulapp@toolness.com
|
||||
|
||||
[Gecko]
|
||||
MinVersion=1.9.2.0
|
||||
MaxVersion=2.0.*
|
|
@ -0,0 +1,349 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
|
||||
|
||||
'use strict';
|
||||
|
||||
// IMPORTANT: Avoid adding any initialization tasks here, if you need to do
|
||||
// something before add-on is loaded consider addon/runner module instead!
|
||||
|
||||
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
|
||||
results: Cr, manager: Cm } = Components;
|
||||
const ioService = Cc['@mozilla.org/network/io-service;1'].
|
||||
getService(Ci.nsIIOService);
|
||||
const resourceHandler = ioService.getProtocolHandler('resource').
|
||||
QueryInterface(Ci.nsIResProtocolHandler);
|
||||
const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
|
||||
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
const prefService = Cc['@mozilla.org/preferences-service;1'].
|
||||
getService(Ci.nsIPrefService).
|
||||
QueryInterface(Ci.nsIPrefBranch);
|
||||
const appInfo = Cc["@mozilla.org/xre/app-info;1"].
|
||||
getService(Ci.nsIXULAppInfo);
|
||||
const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
|
||||
getService(Ci.nsIVersionComparator);
|
||||
|
||||
|
||||
const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
|
||||
'install', 'uninstall', 'upgrade', 'downgrade' ];
|
||||
|
||||
const bind = Function.call.bind(Function.bind);
|
||||
|
||||
let loader = null;
|
||||
let unload = null;
|
||||
let cuddlefishSandbox = null;
|
||||
let nukeTimer = null;
|
||||
|
||||
let resourceDomains = [];
|
||||
function setResourceSubstitution(domain, uri) {
|
||||
resourceDomains.push(domain);
|
||||
resourceHandler.setSubstitution(domain, uri);
|
||||
}
|
||||
|
||||
// Utility function that synchronously reads local resource from the given
|
||||
// `uri` and returns content string.
|
||||
function readURI(uri) {
|
||||
let ioservice = Cc['@mozilla.org/network/io-service;1'].
|
||||
getService(Ci.nsIIOService);
|
||||
let channel = ioservice.newChannel(uri, 'UTF-8', null);
|
||||
let stream = channel.open();
|
||||
|
||||
let cstream = Cc['@mozilla.org/intl/converter-input-stream;1'].
|
||||
createInstance(Ci.nsIConverterInputStream);
|
||||
cstream.init(stream, 'UTF-8', 0, 0);
|
||||
|
||||
let str = {};
|
||||
let data = '';
|
||||
let read = 0;
|
||||
do {
|
||||
read = cstream.readString(0xffffffff, str);
|
||||
data += str.value;
|
||||
} while (read != 0);
|
||||
|
||||
cstream.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// We don't do anything on install & uninstall yet, but in a future
|
||||
// we should allow add-ons to cleanup after uninstall.
|
||||
function install(data, reason) {}
|
||||
function uninstall(data, reason) {}
|
||||
|
||||
function startup(data, reasonCode) {
|
||||
try {
|
||||
let reason = REASON[reasonCode];
|
||||
// URI for the root of the XPI file.
|
||||
// 'jar:' URI if the addon is packed, 'file:' URI otherwise.
|
||||
// (Used by l10n module in order to fetch `locale` folder)
|
||||
let rootURI = data.resourceURI.spec;
|
||||
|
||||
// TODO: Maybe we should perform read harness-options.json asynchronously,
|
||||
// since we can't do anything until 'sessionstore-windows-restored' anyway.
|
||||
let options = JSON.parse(readURI(rootURI + './harness-options.json'));
|
||||
|
||||
let id = options.jetpackID;
|
||||
let name = options.name;
|
||||
|
||||
// Clean the metadata
|
||||
options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {};
|
||||
|
||||
// freeze the permissionss
|
||||
Object.freeze(options.metadata[name]['permissions']);
|
||||
// freeze the metadata
|
||||
Object.freeze(options.metadata[name]);
|
||||
|
||||
// Register a new resource 'domain' for this addon which is mapping to
|
||||
// XPI's `resources` folder.
|
||||
// Generate the domain name by using jetpack ID, which is the extension ID
|
||||
// by stripping common characters that doesn't work as a domain name:
|
||||
let uuidRe =
|
||||
/^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
|
||||
|
||||
let domain = id.
|
||||
toLowerCase().
|
||||
replace(/@/g, '-at-').
|
||||
replace(/\./g, '-dot-').
|
||||
replace(uuidRe, '$1');
|
||||
|
||||
let prefixURI = 'resource://' + domain + '/';
|
||||
let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null);
|
||||
setResourceSubstitution(domain, resourcesURI);
|
||||
|
||||
// Create path to URLs mapping supported by loader.
|
||||
let paths = {
|
||||
// Relative modules resolve to add-on package lib
|
||||
'./': prefixURI + name + '/lib/',
|
||||
'./tests/': prefixURI + name + '/tests/',
|
||||
'': 'resource://gre/modules/commonjs/'
|
||||
};
|
||||
|
||||
// Maps addon lib and tests ressource folders for each package
|
||||
paths = Object.keys(options.metadata).reduce(function(result, name) {
|
||||
result[name + '/'] = prefixURI + name + '/lib/'
|
||||
result[name + '/tests/'] = prefixURI + name + '/tests/'
|
||||
return result;
|
||||
}, paths);
|
||||
|
||||
// We need to map tests folder when we run sdk tests whose package name
|
||||
// is stripped
|
||||
if (name == 'addon-sdk')
|
||||
paths['tests/'] = prefixURI + name + '/tests/';
|
||||
|
||||
let useBundledSDK = options['force-use-bundled-sdk'];
|
||||
if (!useBundledSDK) {
|
||||
try {
|
||||
useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
|
||||
}
|
||||
catch (e) {
|
||||
// Pref doesn't exist, allow using Firefox shipped SDK
|
||||
}
|
||||
}
|
||||
|
||||
// Starting with Firefox 21.0a1, we start using modules shipped into firefox
|
||||
// Still allow using modules from the xpi if the manifest tell us to do so.
|
||||
// And only try to look for sdk modules in xpi if the xpi actually ship them
|
||||
if (options['is-sdk-bundled'] &&
|
||||
(vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
|
||||
// Maps sdk module folders to their resource folder
|
||||
paths[''] = prefixURI + 'addon-sdk/lib/';
|
||||
// test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
|
||||
// so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
|
||||
// until we no longer support SDK modules in XPI:
|
||||
paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
|
||||
}
|
||||
|
||||
// Retrieve list of module folder overloads based on preferences in order to
|
||||
// eventually used a local modules instead of files shipped into Firefox.
|
||||
let branch = prefService.getBranch('extensions.modules.' + id + '.path');
|
||||
paths = branch.getChildList('', {}).reduce(function (result, name) {
|
||||
// Allows overloading of any sub folder by replacing . by / in pref name
|
||||
let path = name.substr(1).split('.').join('/');
|
||||
// Only accept overloading folder by ensuring always ending with `/`
|
||||
if (path) path += '/';
|
||||
let fileURI = branch.getCharPref(name);
|
||||
|
||||
// On mobile, file URI has to end with a `/` otherwise, setSubstitution
|
||||
// takes the parent folder instead.
|
||||
if (fileURI[fileURI.length-1] !== '/')
|
||||
fileURI += '/';
|
||||
|
||||
// Maps the given file:// URI to a resource:// in order to avoid various
|
||||
// failure that happens with file:// URI and be close to production env
|
||||
let resourcesURI = ioService.newURI(fileURI, null, null);
|
||||
let resName = 'extensions.modules.' + domain + '.commonjs.path' + name;
|
||||
setResourceSubstitution(resName, resourcesURI);
|
||||
|
||||
result[path] = 'resource://' + resName + '/';
|
||||
return result;
|
||||
}, paths);
|
||||
|
||||
// Make version 2 of the manifest
|
||||
let manifest = options.manifest;
|
||||
|
||||
// Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
|
||||
let cuddlefishPath = 'loader/cuddlefish.js';
|
||||
let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath;
|
||||
if (paths['sdk/']) { // sdk folder has been overloaded
|
||||
// (from pref, or cuddlefish is still in the xpi)
|
||||
cuddlefishURI = paths['sdk/'] + cuddlefishPath;
|
||||
}
|
||||
else if (paths['']) { // root modules folder has been overloaded
|
||||
cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath;
|
||||
}
|
||||
|
||||
cuddlefishSandbox = loadSandbox(cuddlefishURI);
|
||||
let cuddlefish = cuddlefishSandbox.exports;
|
||||
|
||||
// Normalize `options.mainPath` so that it looks like one that will come
|
||||
// in a new version of linker.
|
||||
let main = options.mainPath;
|
||||
|
||||
unload = cuddlefish.unload;
|
||||
loader = cuddlefish.Loader({
|
||||
paths: paths,
|
||||
// modules manifest.
|
||||
manifest: manifest,
|
||||
|
||||
// Add-on ID used by different APIs as a unique identifier.
|
||||
id: id,
|
||||
// Add-on name.
|
||||
name: name,
|
||||
// Add-on version.
|
||||
version: options.metadata[name].version,
|
||||
// Add-on package descriptor.
|
||||
metadata: options.metadata[name],
|
||||
// Add-on load reason.
|
||||
loadReason: reason,
|
||||
|
||||
prefixURI: prefixURI,
|
||||
// Add-on URI.
|
||||
rootURI: rootURI,
|
||||
// options used by system module.
|
||||
// File to write 'OK' or 'FAIL' (exit code emulation).
|
||||
resultFile: options.resultFile,
|
||||
// Arguments passed as --static-args
|
||||
staticArgs: options.staticArgs,
|
||||
// Add-on preferences branch name
|
||||
preferencesBranch: options.preferencesBranch,
|
||||
|
||||
// Arguments related to test runner.
|
||||
modules: {
|
||||
'@test/options': {
|
||||
iterations: options.iterations,
|
||||
filter: options.filter,
|
||||
profileMemory: options.profileMemory,
|
||||
stopOnError: options.stopOnError,
|
||||
verbose: options.verbose,
|
||||
parseable: options.parseable,
|
||||
checkMemory: options.check_memory,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
|
||||
let require = cuddlefish.Require(loader, module);
|
||||
|
||||
require('sdk/addon/runner').startup(reason, {
|
||||
loader: loader,
|
||||
main: main,
|
||||
prefsURI: rootURI + 'defaults/preferences/prefs.js'
|
||||
});
|
||||
} catch (error) {
|
||||
dump('Bootstrap error: ' +
|
||||
(error.message ? error.message : String(error)) + '\n' +
|
||||
(error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
function loadSandbox(uri) {
|
||||
let proto = {
|
||||
sandboxPrototype: {
|
||||
loadSandbox: loadSandbox,
|
||||
ChromeWorker: ChromeWorker
|
||||
}
|
||||
};
|
||||
let sandbox = Cu.Sandbox(systemPrincipal, proto);
|
||||
// Create a fake commonjs environnement just to enable loading loader.js
|
||||
// correctly
|
||||
sandbox.exports = {};
|
||||
sandbox.module = { uri: uri, exports: sandbox.exports };
|
||||
sandbox.require = function (id) {
|
||||
if (id !== "chrome")
|
||||
throw new Error("Bootstrap sandbox `require` method isn't implemented.");
|
||||
|
||||
return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
|
||||
CC: bind(CC, Components), components: Components,
|
||||
ChromeWorker: ChromeWorker });
|
||||
};
|
||||
scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
|
||||
return sandbox;
|
||||
}
|
||||
|
||||
function unloadSandbox(sandbox) {
|
||||
if ("nukeSandbox" in Cu)
|
||||
Cu.nukeSandbox(sandbox);
|
||||
}
|
||||
|
||||
function setTimeout(callback, delay) {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback({ notify: callback }, delay,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
return timer;
|
||||
}
|
||||
|
||||
function shutdown(data, reasonCode) {
|
||||
let reason = REASON[reasonCode];
|
||||
if (loader) {
|
||||
unload(loader, reason);
|
||||
unload = null;
|
||||
|
||||
// Don't waste time cleaning up if the application is shutting down
|
||||
if (reason != "shutdown") {
|
||||
// Avoid leaking all modules when something goes wrong with one particular
|
||||
// module. Do not clean it up immediatly in order to allow executing some
|
||||
// actions on addon disabling.
|
||||
// We need to keep a reference to the timer, otherwise it is collected
|
||||
// and won't ever fire.
|
||||
nukeTimer = setTimeout(nukeModules, 1000);
|
||||
|
||||
// Bug 944951 - bootstrap.js must remove the added resource: URIs on unload
|
||||
resourceDomains.forEach(domain => {
|
||||
resourceHandler.setSubstitution(domain, null);
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function nukeModules() {
|
||||
nukeTimer = null;
|
||||
// module objects store `exports` which comes from sandboxes
|
||||
// We should avoid keeping link to these object to avoid leaking sandboxes
|
||||
for (let key in loader.modules) {
|
||||
delete loader.modules[key];
|
||||
}
|
||||
// Direct links to sandboxes should be removed too
|
||||
for (let key in loader.sandboxes) {
|
||||
let sandbox = loader.sandboxes[key];
|
||||
delete loader.sandboxes[key];
|
||||
// Bug 775067: From FF17 we can kill all CCW from a given sandbox
|
||||
unloadSandbox(sandbox);
|
||||
}
|
||||
loader = null;
|
||||
|
||||
// both `toolkit/loader` and `system/xul-app` are loaded as JSM's via
|
||||
// `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when
|
||||
// the addon is unload.
|
||||
|
||||
unloadSandbox(cuddlefishSandbox.loaderSandbox);
|
||||
unloadSandbox(cuddlefishSandbox.xulappSandbox);
|
||||
|
||||
// Bug 764840: We need to unload cuddlefish otherwise it will stay alive
|
||||
// and keep a reference to this compartment.
|
||||
unloadSandbox(cuddlefishSandbox);
|
||||
cuddlefishSandbox = null;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0"?>
|
||||
<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>bogus</em:id>
|
||||
<em:type>2</em:type>
|
||||
<em:optionsType>2</em:optionsType>
|
||||
<em:name>bogus</em:name>
|
||||
<em:version>0.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>false</em:unpack>
|
||||
<em:description>bogus</em:description>
|
||||
<em:creator>bogus</em:creator>
|
||||
|
||||
<!-- Mobile -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
|
||||
<em:minVersion>29.0</em:minVersion>
|
||||
<em:maxVersion>33.*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Desktop -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
|
||||
<em:minVersion>29.0</em:minVersion>
|
||||
<em:maxVersion>33.*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<setting id="enable-setting" pref="extensions.janus.enabled" type="bool" title="Enable Janus Proxy"></setting>
|
||||
|
||||
<setting id="url-setting" pref="extensions.janus.pac_url" type="string" title="PAC URL"></setting>
|
||||
|
||||
<setting id="ad-setting" pref="extensions.janus.adblock.enabled" type="bool" title="Enable ad blocking"></setting>
|
||||
<setting id="gifwebm-setting" pref="extensions.janus.gif2video.enabled" type="bool" title="Enable GIF -> Video conversion"></setting>
|
||||
|
||||
<setting type="control" id="bandwidth-setting" title="Bandwidth Stats">
|
||||
<html:span id="bytes-label"></html:span><html:br/>
|
||||
<html:span id="reduction-label"></html:span><html:br/>
|
||||
<html:button id="reset-button"></html:button>
|
||||
</setting>
|
||||
</vbox>
|
|
@ -0,0 +1,12 @@
|
|||
var main = require("./main");
|
||||
|
||||
exports["test main"] = function(assert) {
|
||||
assert.pass("Unit test running!");
|
||||
};
|
||||
|
||||
exports["test main async"] = function(assert, done) {
|
||||
assert.pass("async Unit test running!");
|
||||
done();
|
||||
};
|
||||
|
||||
require("sdk/test").run(exports);
|
Загрузка…
Ссылка в новой задаче