Bug 1890277: part 1) Add CSP parser tests for `require-trusted-types-for`. r=tschuster
[gecko.git] / uriloader / exthandler / tests / HandlerServiceTestUtils.sys.mjs
blob0e52cc2568a81a5328dd68e505be49304020edff
1 /* Any copyright is dedicated to the Public Domain.
2  * http://creativecommons.org/publicdomain/zero/1.0/ */
4 /*
5  * Shared functions for tests related to invoking external handler applications.
6  */
8 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
9 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
11 import { Assert } from "resource://testing-common/Assert.sys.mjs";
13 const lazy = {};
15 XPCOMUtils.defineLazyServiceGetter(
16   lazy,
17   "gExternalProtocolService",
18   "@mozilla.org/uriloader/external-protocol-service;1",
19   "nsIExternalProtocolService"
21 XPCOMUtils.defineLazyServiceGetter(
22   lazy,
23   "gMIMEService",
24   "@mozilla.org/mime;1",
25   "nsIMIMEService"
27 XPCOMUtils.defineLazyServiceGetter(
28   lazy,
29   "gHandlerService",
30   "@mozilla.org/uriloader/handler-service;1",
31   "nsIHandlerService"
34 export var HandlerServiceTestUtils = {
35   /**
36    * Retrieves the names of all the MIME types and protocols configured in the
37    * handler service instance currently under testing.
38    *
39    * @return Array of strings like "example/type" or "example-scheme", sorted
40    *         alphabetically regardless of category.
41    */
42   getAllHandlerInfoTypes() {
43     return Array.from(
44       lazy.gHandlerService.enumerate(),
45       info => info.type
46     ).sort();
47   },
49   /**
50    * Retrieves all the configured handlers for MIME types and protocols.
51    *
52    * @note The nsIHandlerInfo instances returned by the "enumerate" method
53    *       cannot be used for testing because they incorporate information from
54    *       the operating system and also from the default nsIHandlerService
55    *       instance, independently from what instance is under testing.
56    *
57    * @return Array of nsIHandlerInfo instances sorted by their "type" property.
58    */
59   getAllHandlerInfos() {
60     return this.getAllHandlerInfoTypes().map(type => this.getHandlerInfo(type));
61   },
63   /**
64    * Retrieves an nsIHandlerInfo for the given MIME type or protocol, which
65    * incorporates information from the operating system and also from the
66    * handler service instance currently under testing.
67    *
68    * @note If the handler service instance currently under testing is not the
69    *       default one and the requested type is a MIME type, the returned
70    *       nsIHandlerInfo will include information from the default
71    *       nsIHandlerService instance. This cannot be avoided easily because the
72    *       getMIMEInfoFromOS method is not exposed to JavaScript.
73    *
74    * @param type
75    *        MIME type or scheme name of the nsIHandlerInfo to retrieve.
76    *
77    * @return The populated nsIHandlerInfo instance.
78    */
79   getHandlerInfo(type) {
80     if (type.includes("/")) {
81       // We have to use the getFromTypeAndExtension method because we don't have
82       // access to getMIMEInfoFromOS. This means that we have to reset the data
83       // that may have been imported from the default nsIHandlerService instance
84       // and is not overwritten by fillHandlerInfo later.
85       let handlerInfo = lazy.gMIMEService.getFromTypeAndExtension(type, null);
86       if (AppConstants.platform == "android") {
87         // On Android, the first handler application is always the internal one.
88         while (handlerInfo.possibleApplicationHandlers.length > 1) {
89           handlerInfo.possibleApplicationHandlers.removeElementAt(1);
90         }
91       } else {
92         handlerInfo.possibleApplicationHandlers.clear();
93       }
94       handlerInfo.setFileExtensions("");
95       // Populate the object from the handler service instance under testing.
96       if (lazy.gHandlerService.exists(handlerInfo)) {
97         lazy.gHandlerService.fillHandlerInfo(handlerInfo, "");
98       }
99       return handlerInfo;
100     }
102     // Populate the protocol information from the handler service instance under
103     // testing, like the nsIExternalProtocolService::GetProtocolHandlerInfo
104     // method does on the default nsIHandlerService instance.
105     let osDefaultHandlerFound = {};
106     let handlerInfo =
107       lazy.gExternalProtocolService.getProtocolHandlerInfoFromOS(
108         type,
109         osDefaultHandlerFound
110       );
111     if (lazy.gHandlerService.exists(handlerInfo)) {
112       lazy.gHandlerService.fillHandlerInfo(handlerInfo, "");
113     } else {
114       lazy.gExternalProtocolService.setProtocolHandlerDefaults(
115         handlerInfo,
116         osDefaultHandlerFound.value
117       );
118     }
119     return handlerInfo;
120   },
122   /**
123    * Creates an nsIHandlerInfo for the given MIME type or protocol, initialized
124    * to the default values for the current platform.
125    *
126    * @note For this method to work, the specified MIME type or protocol must not
127    *       be configured in the default handler service instance or the one
128    *       under testing, and must not be registered in the operating system.
129    *
130    * @param type
131    *        MIME type or scheme name of the nsIHandlerInfo to create.
132    *
133    * @return The blank nsIHandlerInfo instance.
134    */
135   getBlankHandlerInfo(type) {
136     let handlerInfo = this.getHandlerInfo(type);
138     let preferredAction, preferredApplicationHandler;
139     let alwaysAskBeforeHandling = true;
141     if (AppConstants.platform == "android") {
142       // On Android, the default preferredAction for MIME types is useHelperApp.
143       // For protocols, we always behave as if an operating system provided
144       // handler existed, and as such we initialize them to useSystemDefault.
145       // This is because the AndroidBridge is not available in xpcshell tests.
146       preferredAction = type.includes("/")
147         ? Ci.nsIHandlerInfo.useHelperApp
148         : Ci.nsIHandlerInfo.useSystemDefault;
149       // On Android, the default handler application is always the internal one.
150       preferredApplicationHandler = {
151         name: "Android chooser",
152       };
153     } else {
154       // On Desktop, the default preferredAction for MIME types is saveToDisk,
155       // while for protocols it is alwaysAsk. Since Bug 1735843, for new MIME
156       // types we default to not asking before handling unless a pref is set.
157       alwaysAskBeforeHandling = Services.prefs.getBoolPref(
158         "browser.download.always_ask_before_handling_new_types",
159         false
160       );
162       if (type.includes("/")) {
163         preferredAction = Ci.nsIHandlerInfo.saveToDisk;
164       } else {
165         preferredAction = Ci.nsIHandlerInfo.alwaysAsk;
166         // we'll always ask before handling protocols
167         alwaysAskBeforeHandling = true;
168       }
169       preferredApplicationHandler = null;
170     }
172     this.assertHandlerInfoMatches(handlerInfo, {
173       type,
174       preferredAction,
175       alwaysAskBeforeHandling,
176       preferredApplicationHandler,
177     });
178     return handlerInfo;
179   },
181   /**
182    * Checks whether an nsIHandlerInfo instance matches the provided object.
183    */
184   assertHandlerInfoMatches(handlerInfo, expected) {
185     let expectedInterface = expected.type.includes("/")
186       ? Ci.nsIMIMEInfo
187       : Ci.nsIHandlerInfo;
188     Assert.ok(handlerInfo instanceof expectedInterface);
189     Assert.equal(handlerInfo.type, expected.type);
191     if (!expected.preferredActionOSDependent) {
192       Assert.equal(handlerInfo.preferredAction, expected.preferredAction);
193       Assert.equal(
194         handlerInfo.alwaysAskBeforeHandling,
195         expected.alwaysAskBeforeHandling
196       );
197     }
199     if (expectedInterface == Ci.nsIMIMEInfo) {
200       let fileExtensionsEnumerator = handlerInfo.getFileExtensions();
201       for (let expectedFileExtension of expected.fileExtensions || []) {
202         Assert.equal(fileExtensionsEnumerator.getNext(), expectedFileExtension);
203       }
204       Assert.ok(!fileExtensionsEnumerator.hasMore());
205     }
207     if (expected.preferredApplicationHandler) {
208       this.assertHandlerAppMatches(
209         handlerInfo.preferredApplicationHandler,
210         expected.preferredApplicationHandler
211       );
212     } else {
213       Assert.equal(handlerInfo.preferredApplicationHandler, null);
214     }
216     let handlerAppsArrayEnumerator =
217       handlerInfo.possibleApplicationHandlers.enumerate();
218     if (AppConstants.platform == "android") {
219       // On Android, the first handler application is always the internal one.
220       this.assertHandlerAppMatches(handlerAppsArrayEnumerator.getNext(), {
221         name: "Android chooser",
222       });
223     }
224     for (let expectedHandlerApp of expected.possibleApplicationHandlers || []) {
225       this.assertHandlerAppMatches(
226         handlerAppsArrayEnumerator.getNext(),
227         expectedHandlerApp
228       );
229     }
230     Assert.ok(!handlerAppsArrayEnumerator.hasMoreElements());
231   },
233   /**
234    * Checks whether an nsIHandlerApp instance matches the provided object.
235    */
236   assertHandlerAppMatches(handlerApp, expected) {
237     Assert.ok(handlerApp instanceof Ci.nsIHandlerApp);
238     Assert.equal(handlerApp.name, expected.name);
239     if (expected.executable) {
240       Assert.ok(handlerApp instanceof Ci.nsILocalHandlerApp);
241       Assert.ok(expected.executable.equals(handlerApp.executable));
242     } else if (expected.uriTemplate) {
243       Assert.ok(handlerApp instanceof Ci.nsIWebHandlerApp);
244       Assert.equal(handlerApp.uriTemplate, expected.uriTemplate);
245     } else if (expected.service) {
246       Assert.ok(handlerApp instanceof Ci.nsIDBusHandlerApp);
247       Assert.equal(handlerApp.service, expected.service);
248       Assert.equal(handlerApp.method, expected.method);
249       Assert.equal(handlerApp.dBusInterface, expected.dBusInterface);
250       Assert.equal(handlerApp.objectPath, expected.objectPath);
251     }
252   },