no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / toolkit / modules / ServiceRequest.sys.mjs
bloba88ba9c324d2135603b1d7ad45462d984883c161
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /**
6  * This module consolidates various code and data update requests, so flags
7  * can be set, Telemetry collected, etc. in a central place.
8  */
10 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
12 const lazy = {};
14 XPCOMUtils.defineLazyServiceGetter(
15   lazy,
16   "ProxyService",
17   "@mozilla.org/network/protocol-proxy-service;1",
18   "nsIProtocolProxyService"
21 ChromeUtils.defineESModuleGetters(lazy, {
22   ExtensionPreferencesManager:
23     "resource://gre/modules/ExtensionPreferencesManager.sys.mjs",
24 });
26 XPCOMUtils.defineLazyServiceGetter(
27   lazy,
28   "CaptivePortalService",
29   "@mozilla.org/network/captive-portal-service;1",
30   "nsICaptivePortalService"
33 XPCOMUtils.defineLazyServiceGetter(
34   lazy,
35   "gNetworkLinkService",
36   "@mozilla.org/network/network-link-service;1",
37   "nsINetworkLinkService"
40 const PROXY_CONFIG_TYPES = [
41   "direct",
42   "manual",
43   "pac",
44   "unused", // nsIProtocolProxyService.idl skips index 3.
45   "wpad",
46   "system",
49 function recordEvent(service, source = {}) {
50   try {
51     Services.telemetry.setEventRecordingEnabled("service_request", true);
52     Services.telemetry.recordEvent(
53       "service_request",
54       "bypass",
55       "proxy_info",
56       service,
57       source
58     );
59   } catch (err) {
60     // If the telemetry throws just log the error so it doesn't break any
61     // functionality.
62     console.error(err);
63   }
66 // If proxy.settings is used to change the proxy, an extension will
67 // be "in control".  This returns the id of that extension.
68 async function getControllingExtension() {
69   if (
70     !WebExtensionPolicy.getActiveExtensions().some(p =>
71       p.permissions.includes("proxy")
72     )
73   ) {
74     return undefined;
75   }
76   // Is this proxied by an extension that set proxy prefs?
77   let setting = await lazy.ExtensionPreferencesManager.getSetting(
78     "proxy.settings"
79   );
80   return setting?.id;
83 async function getProxySource(proxyInfo) {
84   // sourceId is set when using proxy.onRequest
85   if (proxyInfo.sourceId) {
86     return {
87       source: proxyInfo.sourceId,
88       type: "api",
89     };
90   }
91   let type = PROXY_CONFIG_TYPES[lazy.ProxyService.proxyConfigType] || "unknown";
93   // If we have a policy it will have set the prefs.
94   if (
95     Services.policies &&
96     Services.policies.status === Services.policies.ACTIVE
97   ) {
98     let policies = Services.policies.getActivePolicies()?.filter(p => p.Proxy);
99     if (policies?.length) {
100       return {
101         source: "policy",
102         type,
103       };
104     }
105   }
107   let source = await getControllingExtension();
108   return {
109     source: source || "prefs",
110     type,
111   };
115  * ServiceRequest is intended to be a drop-in replacement for current users
116  * of XMLHttpRequest.
118  * @param {Object} options - Options for underlying XHR, e.g. { mozAnon: bool }
119  */
120 export class ServiceRequest extends XMLHttpRequest {
121   constructor(options) {
122     super(options);
123   }
124   /**
125    * Opens an XMLHttpRequest, and sets the NSS "beConservative" flag.
126    * Requests are always async.
127    *
128    * @param {String} method - HTTP method to use, e.g. "GET".
129    * @param {String} url - URL to open.
130    * @param {Object} options - Additional options { bypassProxy: bool }.
131    */
132   open(method, url, options) {
133     super.open(method, url, true);
135     if (super.channel instanceof Ci.nsIHttpChannelInternal) {
136       let internal = super.channel.QueryInterface(Ci.nsIHttpChannelInternal);
137       // Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
138       internal.beConservative = true;
139       // Disable use of proxy for this request if necessary.
140       if (options?.bypassProxy && this.bypassProxyEnabled) {
141         internal.bypassProxy = true;
142       }
143     }
144   }
146   get bypassProxy() {
147     let { channel } = this;
148     return channel.QueryInterface(Ci.nsIHttpChannelInternal).bypassProxy;
149   }
151   get isProxied() {
152     let { channel } = this;
153     return !!(channel instanceof Ci.nsIProxiedChannel && channel.proxyInfo);
154   }
156   get bypassProxyEnabled() {
157     return Services.prefs.getBoolPref("network.proxy.allow_bypass", true);
158   }
160   static async logProxySource(channel, service) {
161     if (channel.proxyInfo) {
162       let source = await getProxySource(channel.proxyInfo);
163       recordEvent(service, source);
164     }
165   }
167   static get isOffline() {
168     try {
169       return (
170         Services.io.offline ||
171         lazy.CaptivePortalService.state ==
172           lazy.CaptivePortalService.LOCKED_PORTAL ||
173         !lazy.gNetworkLinkService.isLinkUp
174       );
175     } catch (ex) {
176       // we cannot get state, assume the best.
177     }
178     return false;
179   }