Source code
Revision control
Copy as Markdown
Other Tools
/* 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
/**
* This module consolidates various code and data update requests, so flags
* can be set, Telemetry collected, etc. in a central place.
*/
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
XPCOMUtils.defineLazyServiceGetter(
lazy,
"ProxyService",
"@mozilla.org/network/protocol-proxy-service;1",
"nsIProtocolProxyService"
);
ChromeUtils.defineESModuleGetters(lazy, {
ExtensionPreferencesManager:
"resource://gre/modules/ExtensionPreferencesManager.sys.mjs",
});
XPCOMUtils.defineLazyServiceGetter(
lazy,
"CaptivePortalService",
"@mozilla.org/network/captive-portal-service;1",
"nsICaptivePortalService"
);
XPCOMUtils.defineLazyServiceGetter(
lazy,
"gNetworkLinkService",
"@mozilla.org/network/network-link-service;1",
"nsINetworkLinkService"
);
const PROXY_CONFIG_TYPES = [
"direct",
"manual",
"pac",
"unused", // nsIProtocolProxyService.idl skips index 3.
"wpad",
"system",
];
function recordEvent(service, source = {}) {
try {
source.value = service;
Glean.serviceRequest.bypassProxyInfo.record(source);
} catch (err) {
// If the telemetry throws just log the error so it doesn't break any
// functionality.
console.error(err);
}
}
// If proxy.settings is used to change the proxy, an extension will
// be "in control". This returns the id of that extension.
async function getControllingExtension() {
if (
!WebExtensionPolicy.getActiveExtensions().some(p =>
p.permissions.includes("proxy")
)
) {
return undefined;
}
// Is this proxied by an extension that set proxy prefs?
let setting =
await lazy.ExtensionPreferencesManager.getSetting("proxy.settings");
return setting?.id;
}
async function getProxySource(proxyInfo) {
// sourceId is set when using proxy.onRequest
if (proxyInfo.sourceId) {
return {
source: proxyInfo.sourceId,
type: "api",
};
}
let type = PROXY_CONFIG_TYPES[lazy.ProxyService.proxyConfigType] || "unknown";
// If we have a policy it will have set the prefs.
if (
Services.policies &&
Services.policies.status === Services.policies.ACTIVE
) {
let policies = Services.policies.getActivePolicies()?.filter(p => p.Proxy);
if (policies?.length) {
return {
source: "policy",
type,
};
}
}
let source = await getControllingExtension();
return {
source: source || "prefs",
type,
};
}
/**
* ServiceRequest is intended to be a drop-in replacement for current users
* of XMLHttpRequest.
*
* @param {Object} options - Options for underlying XHR, e.g. { mozAnon: bool }
*/
export class ServiceRequest extends XMLHttpRequest {
constructor(options) {
super(options);
}
/**
* Opens an XMLHttpRequest, and sets the NSS "beConservative" flag.
* Requests are always async.
*
* @param {String} method - HTTP method to use, e.g. "GET".
* @param {String} url - URL to open.
* @param {Object} options - Additional options { bypassProxy: bool }.
*/
open(method, url, options) {
super.open(method, url, true);
if (super.channel instanceof Ci.nsIHttpChannelInternal) {
let internal = super.channel.QueryInterface(Ci.nsIHttpChannelInternal);
// Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
internal.beConservative = true;
// Disable use of proxy for this request if necessary.
if (options?.bypassProxy && this.bypassProxyEnabled) {
internal.bypassProxy = true;
}
}
}
get bypassProxy() {
let { channel } = this;
return channel.QueryInterface(Ci.nsIHttpChannelInternal).bypassProxy;
}
get isProxied() {
let { channel } = this;
return !!(channel instanceof Ci.nsIProxiedChannel && channel.proxyInfo);
}
get bypassProxyEnabled() {
return Services.prefs.getBoolPref("network.proxy.allow_bypass", true);
}
static async logProxySource(channel, service) {
if (channel.proxyInfo) {
let source = await getProxySource(channel.proxyInfo);
recordEvent(service, source);
}
}
static get isOffline() {
try {
return (
Services.io.offline ||
lazy.CaptivePortalService.state ==
lazy.CaptivePortalService.LOCKED_PORTAL ||
!lazy.gNetworkLinkService.isLinkUp
);
} catch (ex) {
// we cannot get state, assume the best.
}
return false;
}
}