Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / toolkit / actors / RemotePageChild.sys.mjs
blobb7cf7b20e9d965f1e8cc275faaf84caf7f229f22
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  * RemotePageChild is a base class for an unprivileged internal page, typically
7  * an about: page. A specific implementation should subclass the RemotePageChild
8  * actor with a more specific actor for that page. Typically, the child is not
9  * needed, but the parent actor will respond to messages and provide results
10  * directly to the page.
11  */
13 const lazy = {};
14 ChromeUtils.defineESModuleGetters(lazy, {
15   AsyncPrefs: "resource://gre/modules/AsyncPrefs.sys.mjs",
16   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
17   RemotePageAccessManager:
18     "resource://gre/modules/RemotePageAccessManager.sys.mjs",
19 });
21 export class RemotePageChild extends JSWindowActorChild {
22   actorCreated() {
23     this.listeners = new Map();
24     this.exportBaseFunctions();
25   }
27   exportBaseFunctions() {
28     const exportableFunctions = [
29       "RPMSendAsyncMessage",
30       "RPMSendQuery",
31       "RPMAddMessageListener",
32       "RPMRemoveMessageListener",
33       "RPMGetIntPref",
34       "RPMGetStringPref",
35       "RPMGetBoolPref",
36       "RPMSetPref",
37       "RPMGetFormatURLPref",
38       "RPMIsWindowPrivate",
39     ];
41     this.exportFunctions(exportableFunctions);
42   }
44   /**
45    * Exports a list of functions to be accessible by the privileged page.
46    * Subclasses may call this function to add functions that are specific
47    * to a page. When the page calls a function, a function with the same
48    * name is called within the child actor.
49    *
50    * Only functions that appear in the whitelist in the
51    * RemotePageAccessManager for that page will be exported.
52    *
53    * @param array of function names.
54    */
55   exportFunctions(functions) {
56     let document = this.document;
57     let principal = document.nodePrincipal;
59     // If there is no content principal, don't export any functions.
60     if (!principal) {
61       return;
62     }
64     let window = this.contentWindow;
66     for (let fnname of functions) {
67       let allowAccess = lazy.RemotePageAccessManager.checkAllowAccessToFeature(
68         principal,
69         fnname,
70         document
71       );
73       if (allowAccess) {
74         // Wrap each function in an access checking function.
75         function accessCheckedFn(...args) {
76           this.checkAllowAccess(fnname, args[0]);
77           return this[fnname](...args);
78         }
80         Cu.exportFunction(accessCheckedFn.bind(this), window, {
81           defineAs: fnname,
82         });
83       }
84     }
85   }
87   handleEvent() {
88     // Do nothing. The DOMDocElementInserted event is just used to create
89     // the actor.
90   }
92   receiveMessage(messagedata) {
93     let message = {
94       name: messagedata.name,
95       data: messagedata.data,
96     };
98     let listeners = this.listeners.get(message.name);
99     if (!listeners) {
100       return;
101     }
103     let clonedMessage = Cu.cloneInto(message, this.contentWindow);
104     for (let listener of listeners.values()) {
105       try {
106         listener(clonedMessage);
107       } catch (e) {
108         console.error(e);
109       }
110     }
111   }
113   wrapPromise(promise) {
114     return new this.contentWindow.Promise((resolve, reject) =>
115       promise.then(resolve, reject)
116     );
117   }
119   /**
120    * Returns true if a feature cannot be accessed by the current page.
121    * Throws an exception if the feature may not be accessed.
123    * @param aDocument child process document to call from
124    * @param aFeature to feature to check access to
125    * @param aValue value that must be included with that feature's whitelist
126    * @returns true if access is allowed or throws an exception otherwise
127    */
128   checkAllowAccess(aFeature, aValue) {
129     let doc = this.document;
130     if (!lazy.RemotePageAccessManager.checkAllowAccess(doc, aFeature, aValue)) {
131       throw new Error(
132         "RemotePageAccessManager does not allow access to " + aFeature
133       );
134     }
136     return true;
137   }
139   addPage(aUrl, aFunctionMap) {
140     lazy.RemotePageAccessManager.addPage(aUrl, aFunctionMap);
141   }
143   // Implementation of functions that are exported into the page.
145   RPMSendAsyncMessage(aName, aData = null) {
146     this.sendAsyncMessage(aName, aData);
147   }
149   RPMSendQuery(aName, aData = null) {
150     return this.wrapPromise(
151       new Promise(resolve => {
152         this.sendQuery(aName, aData).then(result => {
153           resolve(Cu.cloneInto(result, this.contentWindow));
154         });
155       })
156     );
157   }
159   /**
160    * Adds a listener for messages. Many callbacks can be registered for the
161    * same message if necessary. An attempt to register the same callback for the
162    * same message twice will be ignored. When called the callback is passed an
163    * object with these properties:
164    *   name:   The message name
165    *   data:   Any data sent with the message
166    */
167   RPMAddMessageListener(aName, aCallback) {
168     if (!this.listeners.has(aName)) {
169       this.listeners.set(aName, new Set([aCallback]));
170     } else {
171       this.listeners.get(aName).add(aCallback);
172     }
173   }
175   /**
176    * Removes a listener for messages.
177    */
178   RPMRemoveMessageListener(aName, aCallback) {
179     if (!this.listeners.has(aName)) {
180       return;
181     }
183     this.listeners.get(aName).delete(aCallback);
184   }
186   RPMGetIntPref(aPref, defaultValue) {
187     // Only call with a default value if it's defined, to be able to throw
188     // errors for non-existent prefs.
189     if (defaultValue !== undefined) {
190       return Services.prefs.getIntPref(aPref, defaultValue);
191     }
192     return Services.prefs.getIntPref(aPref);
193   }
195   RPMGetStringPref(aPref) {
196     return Services.prefs.getStringPref(aPref);
197   }
199   RPMGetBoolPref(aPref, defaultValue) {
200     // Only call with a default value if it's defined, to be able to throw
201     // errors for non-existent prefs.
202     if (defaultValue !== undefined) {
203       return Services.prefs.getBoolPref(aPref, defaultValue);
204     }
205     return Services.prefs.getBoolPref(aPref);
206   }
208   RPMSetPref(aPref, aVal) {
209     return this.wrapPromise(lazy.AsyncPrefs.set(aPref, aVal));
210   }
212   RPMGetFormatURLPref(aFormatURL) {
213     return Services.urlFormatter.formatURLPref(aFormatURL);
214   }
216   RPMIsWindowPrivate() {
217     return lazy.PrivateBrowsingUtils.isContentWindowPrivate(this.contentWindow);
218   }