зеркало из https://github.com/mozilla/gecko-dev.git
Part 1: Bug 1295807 - Add a Javascript Module for registering proxy scripts. r=kmag,robwu
MozReview-Commit-ID: HLokEivMpmp --HG-- extra : rebase_source : 0d03479790086aba990bb5363884bc3ee1479e69
This commit is contained in:
Родитель
9bb3caade2
Коммит
af0e29d44a
|
@ -543,6 +543,7 @@ class SchemaAPIManager extends EventEmitter {
|
|||
* "addon" - An addon process.
|
||||
* "content" - A content process.
|
||||
* "devtools" - A devtools process.
|
||||
* "proxy" - A proxy script process.
|
||||
*/
|
||||
constructor(processType) {
|
||||
super();
|
||||
|
@ -556,6 +557,7 @@ class SchemaAPIManager extends EventEmitter {
|
|||
content_child: [],
|
||||
devtools_parent: [],
|
||||
devtools_child: [],
|
||||
proxy_script: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -607,12 +609,13 @@ class SchemaAPIManager extends EventEmitter {
|
|||
* the moment - see bugzil.la/1295774.
|
||||
* @param {string} envType Restricts the API to contexts that run in the
|
||||
* given environment. Must be one of the following:
|
||||
* - "addon_parent" - addon APIs that runs in the main process.
|
||||
* - "addon_child" - addon APIs that runs in an addon process.
|
||||
* - "content_parent" - content script APIs that runs in the main process.
|
||||
* - "content_child" - content script APIs that runs in a content process.
|
||||
* - "devtools_parent" - devtools APIs that runs in the main process.
|
||||
* - "devtools_child" - devtools APIs that runs in a devtools process.
|
||||
* - "addon_parent" - addon APIs that run in the main process.
|
||||
* - "addon_child" - addon APIs that run in an addon process.
|
||||
* - "content_parent" - content script APIs that run in the main process.
|
||||
* - "content_child" - content script APIs that run in a content process.
|
||||
* - "devtools_parent" - devtools APIs that run in the main process.
|
||||
* - "devtools_child" - devtools APIs that run in a devtools process.
|
||||
* - "proxy_script" - proxy script APIs that run in the main process.
|
||||
* @param {function(BaseContext)} getAPI A function that returns an object
|
||||
* that will be merged with |chrome| and |browser|. The next example adds
|
||||
* the create, update and remove methods to the tabs API.
|
||||
|
|
|
@ -370,20 +370,15 @@ this.MessageChannel = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns true if the properties of the `data` object match those in
|
||||
* the `filter` object. Matching is done on a strict equality basis,
|
||||
* and the behavior varies depending on the value of the `strict`
|
||||
* parameter.
|
||||
*
|
||||
* @param {object} filter
|
||||
* The filter object to match against.
|
||||
* @param {object} data
|
||||
* The data object being matched.
|
||||
* @param {boolean} [strict=false]
|
||||
* If true, all properties in the `filter` object have a
|
||||
* @param {boolean} [strict=true]
|
||||
* If true, all properties in the `filter` object must have a
|
||||
* corresponding property in `data` with the same value. If
|
||||
* false, properties present in both objects must have the same
|
||||
* value.
|
||||
* value. Matching is done on a strict equality basis.
|
||||
* @returns {boolean} True if the objects match.
|
||||
*/
|
||||
matchesFilter(filter, data, strict = true) {
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ProxyScriptContext"];
|
||||
|
||||
/* exported ProxyScriptContext */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionChild.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
"resource://gre/modules/Schemas.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ProxyService",
|
||||
"@mozilla.org/network/protocol-proxy-service;1",
|
||||
"nsIProtocolProxyService");
|
||||
|
||||
const CATEGORY_EXTENSION_SCRIPTS_CONTENT = "webextension-scripts-content";
|
||||
|
||||
// The length of time (seconds) to wait for a proxy to resolve before ignoring it.
|
||||
const PROXY_TIMEOUT_SEC = 10;
|
||||
|
||||
const {
|
||||
defineLazyGetter,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
BaseContext,
|
||||
LocalAPIImplementation,
|
||||
SchemaAPIManager,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const {
|
||||
Messenger,
|
||||
} = ExtensionChild;
|
||||
|
||||
const PROXY_TYPES = Object.freeze({
|
||||
DIRECT: "direct",
|
||||
HTTPS: "https",
|
||||
PROXY: "proxy",
|
||||
SOCKS: "socks",
|
||||
});
|
||||
|
||||
class ProxyScriptContext extends BaseContext {
|
||||
constructor(extension, url, contextInfo = {}) {
|
||||
super("proxy_script", extension);
|
||||
this.contextInfo = contextInfo;
|
||||
this.extension = extension;
|
||||
this.messageManager = Services.cpmm;
|
||||
this.sandbox = Cu.Sandbox(this.extension.principal, {
|
||||
sandboxName: `proxyscript:${extension.id}:${url}`,
|
||||
metadata: {addonID: extension.id},
|
||||
});
|
||||
this.url = url;
|
||||
this.FindProxyForURL = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and validates a proxy script into the sandbox, and then
|
||||
* registers a new proxy filter for the context.
|
||||
*
|
||||
* @returns {boolean} true if load succeeded; false otherwise.
|
||||
*/
|
||||
load() {
|
||||
Schemas.exportLazyGetter(this.sandbox, "browser", () => this.browserObj);
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(this.url, this.sandbox, "UTF-8");
|
||||
} catch (error) {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: this.normalizeError(error).message,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
this.FindProxyForURL = Cu.unwaiveXrays(this.sandbox.FindProxyForURL);
|
||||
if (typeof this.FindProxyForURL !== "function") {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: "The proxy script must define FindProxyForURL as a function",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
ProxyService.registerFilter(
|
||||
this /* nsIProtocolProxyFilter aFilter */,
|
||||
0 /* unsigned long aPosition */
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get principal() {
|
||||
return this.extension.principal;
|
||||
}
|
||||
|
||||
get cloneScope() {
|
||||
return this.sandbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method (which is required by the nsIProtocolProxyService interface)
|
||||
* is called to apply proxy filter rules for the given URI and proxy object
|
||||
* (or list of proxy objects).
|
||||
*
|
||||
* @param {Object} service A reference to the Protocol Proxy Service.
|
||||
* @param {Object} uri The URI for which these proxy settings apply.
|
||||
* @param {Object} defaultProxyInfo The proxy (or list of proxies) that
|
||||
* would be used by default for the given URI. This may be null.
|
||||
* @returns {Object} The proxy info to apply for the given URI.
|
||||
*/
|
||||
applyFilter(service, uri, defaultProxyInfo) {
|
||||
let ret;
|
||||
try {
|
||||
// Bug 1337001 - provide path and query components to non-https URLs.
|
||||
ret = this.FindProxyForURL(uri.prePath, uri.host, this.contextInfo);
|
||||
} catch (e) {
|
||||
let error = this.normalizeError(e);
|
||||
this.extension.emit("proxy-error", {
|
||||
message: error.message,
|
||||
fileName: error.fileName,
|
||||
lineNumber: error.lineNumber,
|
||||
stack: error.stack,
|
||||
});
|
||||
return defaultProxyInfo;
|
||||
}
|
||||
|
||||
if (!ret || typeof ret !== "string") {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: "FindProxyForURL: Return type must be a string",
|
||||
});
|
||||
return defaultProxyInfo;
|
||||
}
|
||||
|
||||
let rules = ret.split(";");
|
||||
let proxyInfo = this.createProxyInfo(rules);
|
||||
|
||||
return proxyInfo || defaultProxyInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new proxy info object using the return value of FindProxyForURL.
|
||||
*
|
||||
* @param {Array<string>} rules The list of proxy rules returned by FindProxyForURL.
|
||||
* (e.g. ["PROXY 1.2.3.4:8080", "SOCKS 1.1.1.1:9090", "DIRECT"])
|
||||
* @returns {nsIProxyInfo} The proxy info to apply for the given URI.
|
||||
*/
|
||||
createProxyInfo(rules) {
|
||||
if (!rules.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let rule = rules[0].trim();
|
||||
|
||||
if (!rule) {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: "FindProxyForURL: Expected Proxy Rule",
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
let parts = rule.split(/\s+/);
|
||||
if (!parts[0] || parts.length !== 2) {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: `FindProxyForURL: Invalid Proxy Rule: ${rule}`,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
parts[0] = parts[0].toLowerCase();
|
||||
|
||||
switch (parts[0]) {
|
||||
case PROXY_TYPES.PROXY:
|
||||
case PROXY_TYPES.SOCKS:
|
||||
if (!parts[1]) {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: `FindProxyForURL: Missing argument for "${parts[0]}"`,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
let [host, port] = parts[1].split(":");
|
||||
if (!host || !port) {
|
||||
this.extension.emit("proxy-error", {
|
||||
message: `FindProxyForURL: Unable to parse argument for ${rule}`,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
let type = PROXY_TYPES.SOCKS;
|
||||
if (parts[0] == PROXY_TYPES.PROXY) {
|
||||
type = PROXY_TYPES.HTTPS;
|
||||
}
|
||||
|
||||
let failoverProxy = this.createProxyInfo(rules.slice(1));
|
||||
return ProxyService.newProxyInfo(type, host, port, 0,
|
||||
PROXY_TIMEOUT_SEC, failoverProxy);
|
||||
case PROXY_TYPES.DIRECT:
|
||||
return null;
|
||||
default:
|
||||
this.extension.emit("proxy-error", {
|
||||
message: `FindProxyForURL: Unrecognized proxy type: "${parts[0]}"`,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the proxy filter and shuts down the sandbox.
|
||||
*/
|
||||
unload() {
|
||||
super.unload();
|
||||
ProxyService.unregisterFilter(this);
|
||||
Cu.nukeSandbox(this.sandbox);
|
||||
this.sandbox = null;
|
||||
}
|
||||
}
|
||||
|
||||
class ProxyScriptAPIManager extends SchemaAPIManager {
|
||||
constructor() {
|
||||
super("proxy");
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
generateAPIs(...args) {
|
||||
if (!this.initialized) {
|
||||
for (let [/* name */, value] of XPCOMUtils.enumerateCategoryEntries(
|
||||
CATEGORY_EXTENSION_SCRIPTS_CONTENT)) {
|
||||
this.loadScript(value);
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
return super.generateAPIs(...args);
|
||||
}
|
||||
|
||||
registerSchemaAPI(namespace, envType, getAPI) {
|
||||
if (envType == "proxy_script") {
|
||||
super.registerSchemaAPI(namespace, envType, getAPI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProxyScriptInjectionContext {
|
||||
constructor(context, localAPIs) {
|
||||
this.context = context;
|
||||
this.localAPIs = localAPIs;
|
||||
}
|
||||
|
||||
shouldInject(namespace, name, allowedContexts) {
|
||||
if (this.context.envType !== "proxy_script") {
|
||||
throw new Error(`Unexpected context type "${this.context.envType}"`);
|
||||
}
|
||||
|
||||
// Do not generate proxy script APIs unless explicitly allowed.
|
||||
return allowedContexts.includes("proxy");
|
||||
}
|
||||
|
||||
getImplementation(namespace, name) {
|
||||
let obj = namespace.split(".").reduce(
|
||||
(object, prop) => object && object[prop],
|
||||
this.localAPIs);
|
||||
if (obj && name in obj) {
|
||||
return new LocalAPIImplementation(obj, name, this.context);
|
||||
}
|
||||
}
|
||||
|
||||
get cloneScope() {
|
||||
return this.context.cloneScope;
|
||||
}
|
||||
|
||||
get principal() {
|
||||
return this.context.principal;
|
||||
}
|
||||
}
|
||||
|
||||
defineLazyGetter(ProxyScriptContext.prototype, "messenger", function() {
|
||||
let sender = {id: this.extension.uuid, url: this.url};
|
||||
let filter = {extensionId: this.extension.id, toProxyScript: true};
|
||||
return new Messenger(this, [this.messageManager], sender, filter);
|
||||
});
|
||||
|
||||
let proxyScriptAPIManager = new ProxyScriptAPIManager();
|
||||
|
||||
defineLazyGetter(ProxyScriptContext.prototype, "browserObj", function() {
|
||||
let localAPIs = {};
|
||||
proxyScriptAPIManager.generateAPIs(this, localAPIs);
|
||||
|
||||
let browserObj = Cu.createObjectIn(this.sandbox);
|
||||
let injectionContext = new ProxyScriptInjectionContext(this, localAPIs);
|
||||
Schemas.inject(browserObj, injectionContext);
|
||||
return browserObj;
|
||||
});
|
|
@ -20,38 +20,45 @@ function runtimeApiFactory(context) {
|
|||
sendMessage: function(...args) {
|
||||
let options; // eslint-disable-line no-unused-vars
|
||||
let extensionId, message, responseCallback;
|
||||
if (typeof args[args.length - 1] == "function") {
|
||||
if (typeof args[args.length - 1] === "function") {
|
||||
responseCallback = args.pop();
|
||||
}
|
||||
if (!args.length) {
|
||||
return Promise.reject({message: "runtime.sendMessage's message argument is missing"});
|
||||
} else if (args.length == 1) {
|
||||
} else if (args.length === 1) {
|
||||
message = args[0];
|
||||
} else if (args.length == 2) {
|
||||
if (typeof args[0] == "string" && args[0]) {
|
||||
} else if (args.length === 2) {
|
||||
if (typeof args[0] === "string" && args[0]) {
|
||||
[extensionId, message] = args;
|
||||
} else {
|
||||
[message, options] = args;
|
||||
}
|
||||
} else if (args.length == 3) {
|
||||
} else if (args.length === 3) {
|
||||
[extensionId, message, options] = args;
|
||||
} else if (args.length == 4 && !responseCallback) {
|
||||
} else if (args.length === 4 && !responseCallback) {
|
||||
return Promise.reject({message: "runtime.sendMessage's last argument is not a function"});
|
||||
} else {
|
||||
return Promise.reject({message: "runtime.sendMessage received too many arguments"});
|
||||
}
|
||||
|
||||
if (extensionId != null && typeof extensionId != "string") {
|
||||
if (extensionId != null && typeof extensionId !== "string") {
|
||||
return Promise.reject({message: "runtime.sendMessage's extensionId argument is invalid"});
|
||||
}
|
||||
if (options != null && typeof options != "object") {
|
||||
return Promise.reject({message: "runtime.sendMessage's options argument is invalid"});
|
||||
}
|
||||
// TODO(robwu): Validate option keys and values when we support it.
|
||||
|
||||
extensionId = extensionId || extension.id;
|
||||
let recipient = {extensionId};
|
||||
|
||||
if (options != null) {
|
||||
if (typeof options !== "object") {
|
||||
return Promise.reject({message: "runtime.sendMessage's options argument is invalid"});
|
||||
}
|
||||
if (typeof options.toProxyScript === "boolean") {
|
||||
recipient.toProxyScript = options.toProxyScript;
|
||||
} else {
|
||||
return Promise.reject({message: "runtime.sendMessage's options.toProxyScript argument is invalid"});
|
||||
}
|
||||
}
|
||||
|
||||
return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
|
||||
},
|
||||
|
||||
|
@ -92,3 +99,4 @@ function runtimeApiFactory(context) {
|
|||
extensions.registerSchemaAPI("runtime", "addon_child", runtimeApiFactory);
|
||||
extensions.registerSchemaAPI("runtime", "content_child", runtimeApiFactory);
|
||||
extensions.registerSchemaAPI("runtime", "devtools_child", runtimeApiFactory);
|
||||
extensions.registerSchemaAPI("runtime", "proxy_script", runtimeApiFactory);
|
||||
|
|
|
@ -21,6 +21,7 @@ EXTRA_JS_MODULES += [
|
|||
'LegacyExtensionsUtils.jsm',
|
||||
'MessageChannel.jsm',
|
||||
'NativeMessaging.jsm',
|
||||
'ProxyScriptContext.jsm',
|
||||
'Schemas.jsm',
|
||||
]
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
{
|
||||
"namespace": "runtime",
|
||||
"allowedContexts": ["content", "devtools"],
|
||||
"allowedContexts": ["content", "devtools", "proxy"],
|
||||
"description": "Use the <code>browser.runtime</code> API to retrieve the background page, return details about the manifest, and listen for and respond to events in the app or extension lifecycle. You can also use this API to convert the relative path of URLs to fully-qualified URLs.",
|
||||
"types": [
|
||||
{
|
||||
|
@ -331,7 +331,7 @@
|
|||
"name": "sendMessage",
|
||||
"type": "function",
|
||||
"allowAmbiguousOptionalArguments": true,
|
||||
"allowedContexts": ["content", "devtools"],
|
||||
"allowedContexts": ["content", "devtools", "proxy"],
|
||||
"description": "Sends a single message to event listeners within your extension/app or a different extension/app. Similar to $(ref:runtime.connect) but only sends a single message, with an optional response. If sending to your extension, the $(ref:runtime.onMessage) event will be fired in each page, or $(ref:runtime.onMessageExternal), if a different extension. Note that extensions cannot send messages to content scripts using this method. To send messages to content scripts, use $(ref:tabs.sendMessage).",
|
||||
"async": "responseCallback",
|
||||
"parameters": [
|
||||
|
@ -341,7 +341,8 @@
|
|||
"type": "object",
|
||||
"name": "options",
|
||||
"properties": {
|
||||
"includeTlsChannelId": { "type": "boolean", "optional": true, "description": "Whether the TLS channel ID will be passed into onMessageExternal for processes that are listening for the connection event." }
|
||||
"includeTlsChannelId": { "type": "boolean", "optional": true, "description": "Whether the TLS channel ID will be passed into onMessageExternal for processes that are listening for the connection event." },
|
||||
"toProxyScriptSandbox": { "type": "boolean", "optional": true, "description": "If true, the message will be directed to the extension's proxy sandbox."}
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
|
@ -545,7 +546,7 @@
|
|||
{
|
||||
"name": "onMessage",
|
||||
"type": "function",
|
||||
"allowedContexts": ["content", "devtools"],
|
||||
"allowedContexts": ["content", "devtools", "proxy"],
|
||||
"description": "Fired when a message is sent from either an extension process or a content script.",
|
||||
"parameters": [
|
||||
{"name": "message", "type": "any", "optional": true, "description": "The message sent by the calling script."},
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
"use strict";
|
||||
|
||||
/* no-unused-vars": ["error", {"args": "none", "varsIgnorePattern": "^(FindProxyForURL)$"}] */
|
||||
|
||||
Cu.import("resource://gre/modules/Extension.jsm");
|
||||
Cu.import("resource://gre/modules/ProxyScriptContext.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gProxyService",
|
||||
"@mozilla.org/network/protocol-proxy-service;1",
|
||||
"nsIProtocolProxyService");
|
||||
|
||||
function* testProxyScript(options, expected = {}) {
|
||||
let scriptData = String(options.scriptData).replace(/^.*?\{([^]*)\}$/, "$1");
|
||||
let extensionData = {
|
||||
background() {
|
||||
browser.test.onMessage.addListener((message, data) => {
|
||||
if (message === "runtime-message") {
|
||||
browser.runtime.onMessage.addListener((msg, sender, respond) => {
|
||||
if (msg === "finish-from-pac-script") {
|
||||
browser.test.notifyPass("proxy");
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
});
|
||||
browser.runtime.sendMessage(data, {toProxyScript: true}).then(response => {
|
||||
browser.test.sendMessage("runtime-message-sent");
|
||||
});
|
||||
} else if (message === "finish-from-xpcshell-test") {
|
||||
browser.test.notifyPass("proxy");
|
||||
}
|
||||
});
|
||||
},
|
||||
files: {
|
||||
"proxy.js": scriptData,
|
||||
},
|
||||
};
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
let extension_internal = extension.extension;
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
let script = new ProxyScriptContext(extension_internal, extension_internal.getURL("proxy.js"));
|
||||
|
||||
try {
|
||||
yield script.load();
|
||||
} catch (error) {
|
||||
equal(error, expected.error, "Expected error received");
|
||||
script.unload();
|
||||
yield extension.unload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.runtimeMessage) {
|
||||
extension.sendMessage("runtime-message", options.runtimeMessage);
|
||||
yield extension.awaitMessage("runtime-message-sent");
|
||||
} else {
|
||||
extension.sendMessage("finish-from-xpcshell-test");
|
||||
}
|
||||
|
||||
yield extension.awaitFinish("proxy");
|
||||
|
||||
let proxyInfo = yield new Promise((resolve, reject) => {
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: "http://www.mozilla.org/",
|
||||
loadUsingSystemPrincipal: true,
|
||||
});
|
||||
|
||||
gProxyService.asyncResolve(channel, 0, {
|
||||
onProxyAvailable(req, uri, pi, status) {
|
||||
resolve(pi);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (!proxyInfo) {
|
||||
equal(proxyInfo, expected.proxyInfo, "Expected proxyInfo to be null");
|
||||
} else {
|
||||
let expectedProxyInfo = expected.proxyInfo;
|
||||
for (let proxy = proxyInfo; proxy; proxy = proxy.failoverProxy) {
|
||||
equal(proxy.host, expectedProxyInfo.host, `Expected proxy host to be ${expectedProxyInfo.host}`);
|
||||
equal(proxy.port, expectedProxyInfo.port, `Expected proxy port to be ${expectedProxyInfo.port}`);
|
||||
equal(proxy.type, expectedProxyInfo.type, `Expected proxy type to be ${expectedProxyInfo.type}`);
|
||||
expectedProxyInfo = expectedProxyInfo.failoverProxy;
|
||||
}
|
||||
}
|
||||
|
||||
yield extension.unload();
|
||||
script.unload();
|
||||
}
|
||||
|
||||
add_task(function* testUndefinedFindProxyForURL() {
|
||||
yield testProxyScript({
|
||||
scriptData() { },
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testWrongTypeForFindProxyForURL() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
let FindProxyForURL = "foo";
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testInvalidReturnTypeForFindProxyForURL() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testSimpleProxyScript() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
if (host === "www.mozilla.org") {
|
||||
return "DIRECT";
|
||||
}
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testRuntimeErrorInProxyScript() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return RUNTIME_ERROR; // eslint-disable-line no-undef
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyScriptWithUnexpectedReturnType() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return "UNEXPECTED 1.2.3.4:8080";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testSocksReturnType() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
if (host === "www.mozilla.org") {
|
||||
return "SOCKS 4.4.4.4:9002";
|
||||
}
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "4.4.4.4",
|
||||
port: "9002",
|
||||
type: "socks",
|
||||
failoverProxy: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyReturnType() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return "PROXY 1.2.3.4:8080";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "1.2.3.4",
|
||||
port: "8080",
|
||||
type: "https",
|
||||
failoverProxy: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testUnusualWhitespaceForFindProxyForURL() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return " PROXY 1.2.3.4:8080 ";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "1.2.3.4",
|
||||
port: "8080",
|
||||
type: "https",
|
||||
failoverProxy: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testInvalidProxyScriptIgnoresFailover() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return "PROXY 1.2.3.4:8080; UNEXPECTED; SOCKS 1.2.3.4:8080";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "1.2.3.4",
|
||||
port: "8080",
|
||||
type: "https",
|
||||
failoverProxy: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyScriptWithValidFailovers() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return "PROXY 1.2.3.4:8080; SOCKS 4.4.4.4:9000; DIRECT";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "1.2.3.4",
|
||||
port: "8080",
|
||||
type: "https",
|
||||
failoverProxy: {
|
||||
host: "4.4.4.4",
|
||||
port: "9000",
|
||||
type: "socks",
|
||||
failoverProxy: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyScriptWithAnInvalidFailover() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return "PROXY 1.2.3.4:8080; INVALID 1.2.3.4:9090; SOCKS 4.4.4.4:9000; DIRECT";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "1.2.3.4",
|
||||
port: "8080",
|
||||
type: "https",
|
||||
failoverProxy: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyScriptWithEmptyFailovers() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return ";;;;;PROXY 1.2.3.4:8080";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyScriptWithInvalidReturn() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
function FindProxyForURL(url, host) {
|
||||
return "SOCKS :8080;";
|
||||
}
|
||||
},
|
||||
}, {
|
||||
proxyInfo: null,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProxyScriptWithRuntimeUpdate() {
|
||||
yield testProxyScript({
|
||||
scriptData() {
|
||||
let settings = {};
|
||||
function FindProxyForURL(url, host) {
|
||||
if (settings.host === "www.mozilla.org") {
|
||||
return "PROXY 1.2.3.4:8080;";
|
||||
}
|
||||
return "DIRECT";
|
||||
}
|
||||
browser.runtime.onMessage.addListener((msg, sender, respond) => {
|
||||
if (msg.host) {
|
||||
settings.host = msg.host;
|
||||
browser.runtime.sendMessage("finish-from-pac-script");
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
runtimeMessage: {
|
||||
host: "www.mozilla.org",
|
||||
},
|
||||
}, {
|
||||
proxyInfo: {
|
||||
host: "1.2.3.4",
|
||||
port: "8080",
|
||||
type: "https",
|
||||
failoverProxy: null,
|
||||
},
|
||||
});
|
||||
});
|
|
@ -80,3 +80,4 @@ skip-if = os == "android"
|
|||
[test_locale_data.js]
|
||||
[test_native_messaging.js]
|
||||
skip-if = os == "android"
|
||||
[test_proxy_scripts.js]
|
Загрузка…
Ссылка в новой задаче