Bug 1942239 - Add option to explicitly enable incremental origin initialization in...
[gecko.git] / toolkit / components / utils / SimpleServices.sys.mjs
blob943b1e02c80cac125e75a511bb49ffdfff056675
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  * Dumping ground for simple services for which the isolation of a full global
7  * is overkill. Be careful about namespace pollution, and be mindful about
8  * importing lots of JSMs in global scope, since this file will almost certainly
9  * be loaded from enough callsites that any such imports will always end up getting
10  * eagerly loaded at startup.
11  */
13 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
15 const lazy = {};
17 ChromeUtils.defineESModuleGetters(lazy, {
18   NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
19 });
21 XPCOMUtils.defineLazyServiceGetter(
22   lazy,
23   "streamConv",
24   "@mozilla.org/streamConverters;1",
25   "nsIStreamConverterService"
27 const ArrayBufferInputStream = Components.Constructor(
28   "@mozilla.org/io/arraybuffer-input-stream;1",
29   "nsIArrayBufferInputStream",
30   "setData"
34  * This class provides a stream filter for locale messages in CSS files served
35  * by the moz-extension: protocol handler.
36  *
37  * See SubstituteChannel in netwerk/protocol/res/ExtensionProtocolHandler.cpp
38  * for usage.
39  */
40 export function AddonLocalizationConverter() {}
42 AddonLocalizationConverter.prototype = {
43   QueryInterface: ChromeUtils.generateQI(["nsIStreamConverter"]),
45   FROM_TYPE: "application/vnd.mozilla.webext.unlocalized",
46   TO_TYPE: "text/css",
48   checkTypes(aFromType, aToType) {
49     if (aFromType != this.FROM_TYPE) {
50       throw Components.Exception(
51         "Invalid aFromType value",
52         Cr.NS_ERROR_INVALID_ARG,
53         Components.stack.caller.caller
54       );
55     }
56     if (aToType != this.TO_TYPE) {
57       throw Components.Exception(
58         "Invalid aToType value",
59         Cr.NS_ERROR_INVALID_ARG,
60         Components.stack.caller.caller
61       );
62     }
63   },
65   // aContext must be a nsIURI object for a valid moz-extension: URL.
66   getAddon(aContext) {
67     // In this case, we want the add-on ID even if the URL is web accessible,
68     // so check the root rather than the exact path.
69     let uri = Services.io.newURI("/", null, aContext);
71     let addon = WebExtensionPolicy.getByURI(uri);
72     if (!addon) {
73       throw new Components.Exception(
74         "Invalid context",
75         Cr.NS_ERROR_INVALID_ARG
76       );
77     }
78     return addon;
79   },
81   convertToStream(aAddon, aString) {
82     aString = aAddon.localize(aString);
83     let bytes = new TextEncoder().encode(aString).buffer;
84     return new ArrayBufferInputStream(bytes, 0, bytes.byteLength);
85   },
87   convert(aStream, aFromType, aToType, aContext) {
88     this.checkTypes(aFromType, aToType);
89     let addon = this.getAddon(aContext);
91     let count = aStream.available();
92     let string = count
93       ? new TextDecoder().decode(lazy.NetUtil.readInputStream(aStream, count))
94       : "";
95     return this.convertToStream(addon, string);
96   },
98   asyncConvertData(aFromType, aToType, aListener, aContext) {
99     this.checkTypes(aFromType, aToType);
100     this.addon = this.getAddon(aContext);
101     this.listener = aListener;
102   },
104   onStartRequest() {
105     this.parts = [];
106     this.decoder = new TextDecoder();
107   },
109   onDataAvailable(aRequest, aInputStream, aOffset, aCount) {
110     let bytes = lazy.NetUtil.readInputStream(aInputStream, aCount);
111     this.parts.push(this.decoder.decode(bytes, { stream: true }));
112   },
114   onStopRequest(aRequest, aStatusCode) {
115     try {
116       this.listener.onStartRequest(aRequest, null);
117       if (Components.isSuccessCode(aStatusCode)) {
118         this.parts.push(this.decoder.decode());
119         let string = this.parts.join("");
120         let stream = this.convertToStream(this.addon, string);
122         this.listener.onDataAvailable(aRequest, stream, 0, stream.available());
123       }
124     } catch (e) {
125       aStatusCode = e.result || Cr.NS_ERROR_FAILURE;
126     }
127     this.listener.onStopRequest(aRequest, aStatusCode);
128   },
131 export function HttpIndexViewer() {}
133 HttpIndexViewer.prototype = {
134   QueryInterface: ChromeUtils.generateQI(["nsIDocumentLoaderFactory"]),
136   createInstance(
137     aCommand,
138     aChannel,
139     aLoadGroup,
140     aContentType,
141     aContainer,
142     aExtraInfo,
143     aDocListenerResult
144   ) {
145     // Bug 1824325: application/http-index-format is deprecated for almost all
146     // sites, we only allow it for urls with a inner scheme of "file" or
147     // "moz-gio" (specified in network.http_index_format.allowed_schemes).
148     // This also includes jar: and resource:// uris, as jar: uris has a inner
149     // scheme of "file", and resource:// uris have been turned into either a
150     // jar: or file:// uri by the point where we are checking them here.
152     let uri = aChannel.URI;
153     if (uri instanceof Ci.nsINestedURI) {
154       uri = uri.QueryInterface(Ci.nsINestedURI).innermostURI;
155     }
157     const allowedSchemes = Services.prefs.getStringPref(
158       "network.http_index_format.allowed_schemes",
159       ""
160     );
161     let isFile =
162       allowedSchemes === "*" || allowedSchemes.split(",").some(uri.schemeIs);
163     let contentType = isFile ? "text/html" : "text/plain";
165     aChannel.contentType = contentType;
167     // NOTE: This assumes that both text/html and text/plain will continue to be
168     // handled by nsContentDLF. If this ever changes this logic will need to be
169     // updated.
170     let factory = Cc[
171       "@mozilla.org/content/document-loader-factory;1"
172     ].getService(Ci.nsIDocumentLoaderFactory);
174     let listener = {};
175     let res = factory.createInstance(
176       "view",
177       aChannel,
178       aLoadGroup,
179       contentType,
180       aContainer,
181       aExtraInfo,
182       listener
183     );
185     if (isFile) {
186       aDocListenerResult.value = lazy.streamConv.asyncConvertData(
187         "application/http-index-format",
188         "text/html",
189         listener.value,
190         null
191       );
192     } else {
193       aDocListenerResult.value = listener.value;
194       aChannel.loadInfo.browsingContext.window.console.warn(
195         "application/http-index-format is deprecated, content will display as plain text"
196       );
197     }
199     return res;
200   },