Part 4:
Bug 1295807 - Add in the implementation for the network API. r?kmag
MozReview-Commit-ID: 8OUdJVyhTvc
--- a/toolkit/components/extensions/ProxyScript.jsm
+++ b/toolkit/components/extensions/ProxyScript.jsm
@@ -34,74 +34,89 @@ const PROXY_TYPES = Object.freeze({
SOCKS4: "socks4",
SOCKS5: "socks5",
PASS: "pass",
});
const FAILOVER_TIMEOUT_SEC = 10;
class ProxyFilter {
- constructor(sandbox, contextInfo) {
+ constructor(sandbox, extension, contextInfo) {
this.sandbox = sandbox;
+ this.extension = extension;
this.contextInfo = contextInfo;
}
applyFilter(service, uri, defaultProxyInfo) {
let ret;
try {
let code = this.generateCode(uri);
ret = Components.utils.evalInSandbox(code, this.sandbox);
} catch (e) {
+ this.extension.emit("network-error", new Error("FindProxyForURL: Failed to execute proxy script"));
Cu.reportError(e);
return defaultProxyInfo;
}
if (!ret || typeof ret != "string") {
+ this.extension.emit("network-error", new Error("FindProxyForURL: Return type must be a string"));
Cu.reportError(`ProxyScript: Invalid Return Type: ${ret}`);
return defaultProxyInfo;
}
let rules = ret.split(";");
let proxyInfo = this.parseRule(rules.shift(), rules);
return proxyInfo || defaultProxyInfo;
}
parseRule(rule, failoverRules) {
rule = rule.trim();
if (!rule) {
+ this.extension.emit("network-error", new Error(
+ "FindProxyForUrl: Invalid Proxy Rule"
+ ));
Cu.reportError("ProxyScript: Invalid Proxy Rule");
return null;
}
let parts = rule.split(" ");
if (!parts[0]) {
+ this.extension.emit("network-error", new Error(
+ `FindProxyForUrl: Invalid Proxy Rule: ${rule}`
+ ));
Cu.reportError(`ProxyScript: Invalid Proxy Rule: ${rule}`);
return null;
}
parts[0] = parts[0].toLowerCase();
let host, port, type, timeout;
// TODO(matt): Add support for "SYSTEM" and "WPAD" return types.
switch (parts[0]) {
case PROXY_TYPES.PROXY:
case PROXY_TYPES.SOCKS:
if (!parts[1]) {
- Cu.reportError(`ProxyScript: Invalid Value for Proxy Key: ${parts[0]}`);
+ this.extension.emit("network-error", new Error(
+ `FindProxyForUrl: Missing argument for ${parts[0]}`
+ ));
+ Cu.reportError(`FindProxyForURL: Invalid Value for Proxy Key: ${parts[0]}`);
return null;
}
[host, port] = parts[1].split(":");
if (!host || !port) {
- Cu.reportError(`ProxyScript: Invalid Value for Proxy Key: ${parts[0]}`);
+ this.extension.emit("network-error", new Error(
+ `FindProxyForUrl: Unable to parse arguments for ${parts[0]} type: ${parts[1]}`
+ ));
+ Cu.reportError(`FindProxyForURL: Invalid Value for Proxy Key: ${parts[0]}`);
return null;
}
timeout = FAILOVER_TIMEOUT_SEC;
type = PROXY_TYPES.SOCKS;
if (parts[0] == PROXY_TYPES.PROXY) {
@@ -114,16 +129,19 @@ class ProxyFilter {
}
return gProxyService.newProxyInfo(type, host, port, 0, timeout, failoverProxyInfo);
case PROXY_TYPES.PASS:
return null;
case PROXY_TYPES.DIRECT:
return gProxyService.newProxyInfo("direct", "", -1, 0, 0, null);
default:
+ this.extension.emit("network-error", new Error(
+ `FindProxyForURL: Unknown return type: ${parts[0]}`
+ ));
Cu.reportError(`ProxyScript: Invalid Type for Proxy Rule: ${parts[0]}`);
return null;
}
}
/**
* Constructs a function call to `FindProxyForURL` for evaluation in the proxy sandbox.
*
@@ -155,17 +173,17 @@ class ProxyFilter {
}
class ProxyScriptContext extends BaseContext {
constructor(extension, url, contextInfo = {}) {
super("proxy_script", extension);
this.principal_ = extension.principal;
this.sandbox_ = Cu.Sandbox(this.principal_);
- this.filter_ = new ProxyFilter(this.sandbox_, contextInfo);
+ this.filter_ = new ProxyFilter(this.sandbox_, extension, contextInfo);
this.url_ = extension.getURL(url);
this.messageManager = Services.ppmm;
let sender = {id: extension.uuid, url: this.url_};
// Only allow messages to the sandbox if the sender uses this filter.
let filter = {extensionId: extension.id, toProxyScriptSandbox: true};
@@ -178,22 +196,24 @@ class ProxyScriptContext extends BaseCon
this /* context */,
false /* isChromeCompat */,
browserObj /* dest */
);
try {
Services.scriptloader.loadSubScript(this.url_, this.sandbox_, "UTF-8");
} catch (error) {
+ this.extension.emit("network-error", error);
Cu.reportError("ProxyScript: Invalid Proxy Script");
return;
}
let {FindProxyForURL} = this.sandbox_;
if (typeof FindProxyForURL != "function") {
+ this.extension.emit("network-error", new Error("FindProxyForURL must be a function"));
Cu.reportError("ProxyScript: Invalid Proxy Script");
return;
}
gProxyService.registerFilter(
this.filter_ /* nsIProtocolProxyFilter aFilter */,
0 /* unsigned long aPosition */
);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/ext-network.js
@@ -0,0 +1,53 @@
+"use strict";
+
+var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "ProxyScript",
+ "resource://gre/modules/ProxyScript.jsm");
+
+var {EventManager} = ExtensionUtils;
+
+// WeakMap[Extension -> ProxyScript]
+let proxyScriptMap = new Map();
+
+/* eslint-disable mozilla/balanced-listeners */
+extensions.on("shutdown", (type, extension) => {
+ let proxyScript = proxyScriptMap.get(extension);
+ if (proxyScript) {
+ proxyScript.unload();
+ proxyScriptMap.delete(extension);
+ }
+});
+/* eslint-enable mozilla/balanced-listeners */
+
+extensions.registerSchemaAPI("network", "addon_parent", context => {
+ let {extension} = context;
+ return {
+ network: {
+ setProxyScript: (url) => {
+ let proxyScript;
+ if (proxyScriptMap.has(extension)) {
+ proxyScript = proxyScriptMap.get(extension);
+ proxyScript.unload();
+ proxyScriptMap.delete(extension);
+ }
+
+ proxyScript = new ProxyScript(extension, url);
+ proxyScript.load();
+ proxyScriptMap.set(extension, proxyScript);
+ return Promise.resolve();
+ },
+ onProxyError: new EventManager(context, "network.onProxyError", fire => {
+ let listener = (name, error) => {
+ fire(error.message);
+ };
+ extension.on("network-error", listener);
+ return () => {
+ extension.off("network-error", listener);
+ };
+ }).api(),
+ },
+ };
+});
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -1,14 +1,15 @@
# scripts
category webextension-scripts alarms chrome://extensions/content/ext-alarms.js
category webextension-scripts backgroundPage chrome://extensions/content/ext-backgroundPage.js
category webextension-scripts cookies chrome://extensions/content/ext-cookies.js
category webextension-scripts downloads chrome://extensions/content/ext-downloads.js
category webextension-scripts management chrome://extensions/content/ext-management.js
+category webextension-scripts network chrome://extensions/content/ext-network.js
category webextension-scripts notifications chrome://extensions/content/ext-notifications.js
category webextension-scripts i18n chrome://extensions/content/ext-i18n.js
category webextension-scripts idle chrome://extensions/content/ext-idle.js
category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
category webextension-scripts runtime chrome://extensions/content/ext-runtime.js
category webextension-scripts extension chrome://extensions/content/ext-extension.js
category webextension-scripts storage chrome://extensions/content/ext-storage.js
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -7,16 +7,17 @@ toolkit.jar:
content/extensions/ext-alarms.js
content/extensions/ext-backgroundPage.js
content/extensions/ext-cookies.js
content/extensions/ext-downloads.js
content/extensions/ext-management.js
content/extensions/ext-notifications.js
content/extensions/ext-i18n.js
content/extensions/ext-idle.js
+ content/extensions/ext-network.js
content/extensions/ext-webRequest.js
content/extensions/ext-webNavigation.js
content/extensions/ext-runtime.js
content/extensions/ext-extension.js
content/extensions/ext-storage.js
content/extensions/ext-test.js
content/extensions/ext-c-extension.js
content/extensions/ext-c-runtime.js