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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5"use strict";
6
7const { XPCOMUtils } = ChromeUtils.import(
8  "resource://gre/modules/XPCOMUtils.jsm"
9);
10const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
11const { AppConstants } = ChromeUtils.import(
12  "resource://gre/modules/AppConstants.jsm"
13);
14
15XPCOMUtils.defineLazyServiceGetters(this, {
16  gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
17  gExternalProtocolService: [
18    "@mozilla.org/uriloader/external-protocol-service;1",
19    "nsIExternalProtocolService",
20  ],
21  gHandlerService: [
22    "@mozilla.org/uriloader/handler-service;1",
23    "nsIHandlerService",
24  ],
25  gMIMEService: ["@mozilla.org/mime;1", "nsIMIMEService"],
26  gXulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"],
27});
28
29XPCOMUtils.defineLazyModuleGetters(this, {
30  AddonManager: "resource://gre/modules/AddonManager.jsm",
31  BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
32  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
33  FileUtils: "resource://gre/modules/FileUtils.jsm",
34  ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
35  WebsiteFilter: "resource:///modules/policies/WebsiteFilter.jsm",
36});
37
38XPCOMUtils.defineLazyGlobalGetters(this, ["File", "FileReader"]);
39
40const PREF_LOGLEVEL = "browser.policies.loglevel";
41const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL;
42
43XPCOMUtils.defineLazyGetter(this, "log", () => {
44  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
45  return new ConsoleAPI({
46    prefix: "Policies.jsm",
47    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
48    // messages during development. See LOG_LEVELS in Console.jsm for details.
49    maxLogLevel: "error",
50    maxLogLevelPref: PREF_LOGLEVEL,
51  });
52});
53
54var EXPORTED_SYMBOLS = ["Policies"];
55
56/*
57 * ============================
58 * = POLICIES IMPLEMENTATIONS =
59 * ============================
60 *
61 * The Policies object below is where the implementation for each policy
62 * happens. An object for each policy should be defined, containing
63 * callback functions that will be called by the engine.
64 *
65 * See the _callbacks object in EnterprisePolicies.js for the list of
66 * possible callbacks and an explanation of each.
67 *
68 * Each callback will be called with two parameters:
69 * - manager
70 *   This is the EnterprisePoliciesManager singleton object from
71 *   EnterprisePolicies.js
72 *
73 * - param
74 *   The parameter defined for this policy in policies-schema.json.
75 *   It will be different for each policy. It could be a boolean,
76 *   a string, an array or a complex object. All parameters have
77 *   been validated according to the schema, and no unknown
78 *   properties will be present on them.
79 *
80 * The callbacks will be bound to their parent policy object.
81 */
82var Policies = {
83  "3rdparty": {
84    onBeforeAddons(manager, param) {
85      manager.setExtensionPolicies(param.Extensions);
86    },
87  },
88
89  AppAutoUpdate: {
90    onBeforeUIStartup(manager, param) {
91      // Logic feels a bit reversed here, but it's correct. If AppAutoUpdate is
92      // true, we disallow turning off auto updating, and visa versa.
93      if (param) {
94        manager.disallowFeature("app-auto-updates-off");
95      } else {
96        manager.disallowFeature("app-auto-updates-on");
97      }
98    },
99  },
100
101  AppUpdateURL: {
102    // No implementation needed here. UpdateService.jsm will check for this
103    // policy directly when determining the update URL.
104  },
105
106  Authentication: {
107    onBeforeAddons(manager, param) {
108      let locked = true;
109      if ("Locked" in param) {
110        locked = param.Locked;
111      }
112
113      if ("SPNEGO" in param) {
114        setDefaultPref(
115          "network.negotiate-auth.trusted-uris",
116          param.SPNEGO.join(", "),
117          locked
118        );
119      }
120      if ("Delegated" in param) {
121        setDefaultPref(
122          "network.negotiate-auth.delegation-uris",
123          param.Delegated.join(", "),
124          locked
125        );
126      }
127      if ("NTLM" in param) {
128        setDefaultPref(
129          "network.automatic-ntlm-auth.trusted-uris",
130          param.NTLM.join(", "),
131          locked
132        );
133      }
134      if ("AllowNonFQDN" in param) {
135        if ("NTLM" in param.AllowNonFQDN) {
136          setDefaultPref(
137            "network.automatic-ntlm-auth.allow-non-fqdn",
138            param.AllowNonFQDN.NTLM,
139            locked
140          );
141        }
142        if ("SPNEGO" in param.AllowNonFQDN) {
143          setDefaultPref(
144            "network.negotiate-auth.allow-non-fqdn",
145            param.AllowNonFQDN.SPNEGO,
146            locked
147          );
148        }
149      }
150      if ("AllowProxies" in param) {
151        if ("NTLM" in param.AllowProxies) {
152          setDefaultPref(
153            "network.automatic-ntlm-auth.allow-proxies",
154            param.AllowProxies.NTLM,
155            locked
156          );
157        }
158        if ("SPNEGO" in param.AllowProxies) {
159          setDefaultPref(
160            "network.negotiate-auth.allow-proxies",
161            param.AllowProxies.SPNEGO,
162            locked
163          );
164        }
165      }
166      if ("PrivateBrowsing" in param) {
167        setDefaultPref(
168          "network.auth.private-browsing-sso",
169          param.PrivateBrowsing,
170          locked
171        );
172      }
173    },
174  },
175
176  BlockAboutAddons: {
177    onBeforeUIStartup(manager, param) {
178      if (param) {
179        blockAboutPage(manager, "about:addons", true);
180      }
181    },
182  },
183
184  BlockAboutConfig: {
185    onBeforeUIStartup(manager, param) {
186      if (param) {
187        blockAboutPage(manager, "about:config");
188        setAndLockPref("devtools.chrome.enabled", false);
189      }
190    },
191  },
192
193  BlockAboutProfiles: {
194    onBeforeUIStartup(manager, param) {
195      if (param) {
196        blockAboutPage(manager, "about:profiles");
197      }
198    },
199  },
200
201  BlockAboutSupport: {
202    onBeforeUIStartup(manager, param) {
203      if (param) {
204        blockAboutPage(manager, "about:support");
205      }
206    },
207  },
208
209  Bookmarks: {
210    onAllWindowsRestored(manager, param) {
211      BookmarksPolicies.processBookmarks(param);
212    },
213  },
214
215  CaptivePortal: {
216    onBeforeAddons(manager, param) {
217      setAndLockPref("network.captive-portal-service.enabled", param);
218    },
219  },
220
221  Certificates: {
222    onBeforeAddons(manager, param) {
223      if ("ImportEnterpriseRoots" in param) {
224        setAndLockPref(
225          "security.enterprise_roots.enabled",
226          param.ImportEnterpriseRoots
227        );
228      }
229      if ("Install" in param) {
230        (async () => {
231          let dirs = [];
232          let platform = AppConstants.platform;
233          if (platform == "win") {
234            dirs = [
235              // Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
236              Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
237              // Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
238              Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
239            ];
240          } else if (platform == "macosx" || platform == "linux") {
241            dirs = [
242              // These two keys are named wrong. They return the Mozilla directory.
243              Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile),
244              Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile),
245            ];
246          }
247          dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile));
248          for (let certfilename of param.Install) {
249            let certfile;
250            try {
251              certfile = Cc["@mozilla.org/file/local;1"].createInstance(
252                Ci.nsIFile
253              );
254              certfile.initWithPath(certfilename);
255            } catch (e) {
256              for (let dir of dirs) {
257                certfile = dir.clone();
258                certfile.append(
259                  platform == "linux" ? "certificates" : "Certificates"
260                );
261                certfile.append(certfilename);
262                if (certfile.exists()) {
263                  break;
264                }
265              }
266            }
267            let file;
268            try {
269              file = await File.createFromNsIFile(certfile);
270            } catch (e) {
271              log.error(`Unable to find certificate - ${certfilename}`);
272              continue;
273            }
274            let reader = new FileReader();
275            reader.onloadend = function() {
276              if (reader.readyState != reader.DONE) {
277                log.error(`Unable to read certificate - ${certfile.path}`);
278                return;
279              }
280              let certFile = reader.result;
281              let certFileArray = [];
282              for (let i = 0; i < certFile.length; i++) {
283                certFileArray.push(certFile.charCodeAt(i));
284              }
285              let cert;
286              try {
287                cert = gCertDB.constructX509(certFileArray);
288              } catch (e) {
289                log.debug(
290                  `constructX509 failed with error '${e}' - trying constructX509FromBase64.`
291                );
292                try {
293                  // It might be PEM instead of DER.
294                  cert = gCertDB.constructX509FromBase64(pemToBase64(certFile));
295                } catch (ex) {
296                  log.error(`Unable to add certificate - ${certfile.path}`, ex);
297                }
298              }
299              if (cert) {
300                if (
301                  gCertDB.isCertTrusted(
302                    cert,
303                    Ci.nsIX509Cert.CA_CERT,
304                    Ci.nsIX509CertDB.TRUSTED_SSL
305                  )
306                ) {
307                  // Certificate is already installed.
308                  return;
309                }
310                try {
311                  gCertDB.addCert(certFile, "CT,CT,");
312                } catch (e) {
313                  // It might be PEM instead of DER.
314                  gCertDB.addCertFromBase64(pemToBase64(certFile), "CT,CT,");
315                }
316              }
317            };
318            reader.readAsBinaryString(file);
319          }
320        })();
321      }
322    },
323  },
324
325  Cookies: {
326    onBeforeUIStartup(manager, param) {
327      addAllowDenyPermissions("cookie", param.Allow, param.Block);
328
329      if (param.AllowSession) {
330        for (let origin of param.AllowSession) {
331          try {
332            Services.perms.addFromPrincipal(
333              Services.scriptSecurityManager.createContentPrincipalFromOrigin(
334                origin
335              ),
336              "cookie",
337              Ci.nsICookiePermission.ACCESS_SESSION,
338              Ci.nsIPermissionManager.EXPIRE_POLICY
339            );
340          } catch (ex) {
341            log.error(
342              `Unable to add cookie session permission - ${origin.href}`
343            );
344          }
345        }
346      }
347
348      if (param.Block) {
349        const hosts = param.Block.map(url => url.hostname)
350          .sort()
351          .join("\n");
352        runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
353          for (let blocked of param.Block) {
354            Services.cookies.removeCookiesWithOriginAttributes(
355              "{}",
356              blocked.hostname
357            );
358          }
359        });
360      }
361
362      if (
363        param.Default !== undefined ||
364        param.AcceptThirdParty !== undefined ||
365        param.RejectTracker !== undefined ||
366        param.Locked
367      ) {
368        const ACCEPT_COOKIES = 0;
369        const REJECT_THIRD_PARTY_COOKIES = 1;
370        const REJECT_ALL_COOKIES = 2;
371        const REJECT_UNVISITED_THIRD_PARTY = 3;
372        const REJECT_TRACKER = 4;
373
374        let newCookieBehavior = ACCEPT_COOKIES;
375        if (param.Default !== undefined && !param.Default) {
376          newCookieBehavior = REJECT_ALL_COOKIES;
377        } else if (param.AcceptThirdParty) {
378          if (param.AcceptThirdParty == "never") {
379            newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
380          } else if (param.AcceptThirdParty == "from-visited") {
381            newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
382          }
383        } else if (param.RejectTracker !== undefined && param.RejectTracker) {
384          newCookieBehavior = REJECT_TRACKER;
385        }
386
387        setDefaultPref(
388          "network.cookie.cookieBehavior",
389          newCookieBehavior,
390          param.Locked
391        );
392      }
393
394      const KEEP_COOKIES_UNTIL_EXPIRATION = 0;
395      const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2;
396
397      if (param.ExpireAtSessionEnd !== undefined || param.Locked) {
398        let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION;
399        if (param.ExpireAtSessionEnd) {
400          newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION;
401        }
402
403        setDefaultPref(
404          "network.cookie.lifetimePolicy",
405          newLifetimePolicy,
406          param.Locked
407        );
408      }
409    },
410  },
411
412  DefaultDownloadDirectory: {
413    onBeforeAddons(manager, param) {
414      setDefaultPref("browser.download.dir", replacePathVariables(param));
415      // If a custom download directory is being used, just lock folder list to 2.
416      setAndLockPref("browser.download.folderList", 2);
417    },
418  },
419
420  DisableAppUpdate: {
421    onBeforeAddons(manager, param) {
422      if (param) {
423        manager.disallowFeature("appUpdate");
424      }
425    },
426  },
427
428  DisableBuiltinPDFViewer: {
429    onBeforeAddons(manager, param) {
430      if (param) {
431        setAndLockPref("pdfjs.disabled", true);
432      }
433    },
434  },
435
436  DisabledCiphers: {
437    onBeforeAddons(manager, param) {
438      if ("TLS_DHE_RSA_WITH_AES_128_CBC_SHA" in param) {
439        setAndLockPref(
440          "security.ssl3.dhe_rsa_aes_128_sha",
441          !param.TLS_DHE_RSA_WITH_AES_128_CBC_SHA
442        );
443      }
444      if ("TLS_DHE_RSA_WITH_AES_256_CBC_SHA" in param) {
445        setAndLockPref(
446          "security.ssl3.dhe_rsa_aes_256_sha",
447          !param.TLS_DHE_RSA_WITH_AES_256_CBC_SHA
448        );
449      }
450      if ("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" in param) {
451        setAndLockPref(
452          "security.ssl3.ecdhe_rsa_aes_128_sha",
453          !param.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
454        );
455      }
456      if ("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" in param) {
457        setAndLockPref(
458          "security.ssl3.ecdhe_rsa_aes_256_sha",
459          !param.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
460        );
461      }
462      if ("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" in param) {
463        setAndLockPref(
464          "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
465          !param.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
466        );
467      }
468      if ("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" in param) {
469        setAndLockPref(
470          "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
471          !param.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
472        );
473      }
474      if ("TLS_RSA_WITH_AES_128_CBC_SHA" in param) {
475        setAndLockPref(
476          "security.ssl3.rsa_aes_128_sha",
477          !param.TLS_RSA_WITH_AES_128_CBC_SHA
478        );
479      }
480      if ("TLS_RSA_WITH_AES_256_CBC_SHA" in param) {
481        setAndLockPref(
482          "security.ssl3.rsa_aes_256_sha",
483          !param.TLS_RSA_WITH_AES_256_CBC_SHA
484        );
485      }
486      if ("TLS_RSA_WITH_3DES_EDE_CBC_SHA" in param) {
487        setAndLockPref(
488          "security.ssl3.rsa_des_ede3_sha",
489          !param.TLS_RSA_WITH_3DES_EDE_CBC_SHA
490        );
491      }
492      if ("TLS_RSA_WITH_AES_128_GCM_SHA256" in param) {
493        setAndLockPref(
494          "security.ssl3.rsa_aes_128_gcm_sha256",
495          !param.TLS_RSA_WITH_AES_128_GCM_SHA256
496        );
497      }
498      if ("TLS_RSA_WITH_AES_256_GCM_SHA384" in param) {
499        setAndLockPref(
500          "security.ssl3.rsa_aes_256_gcm_sha384",
501          !param.TLS_RSA_WITH_AES_256_GCM_SHA384
502        );
503      }
504    },
505  },
506
507  DisableDefaultBrowserAgent: {
508    // The implementation of this policy is in the default browser agent itself
509    // (/toolkit/mozapps/defaultagent); we need an entry for it here so that it
510    // shows up in about:policies as a real policy and not as an error.
511  },
512
513  DisableDeveloperTools: {
514    onBeforeAddons(manager, param) {
515      if (param) {
516        setAndLockPref("devtools.policy.disabled", true);
517        setAndLockPref("devtools.chrome.enabled", false);
518
519        manager.disallowFeature("devtools");
520        blockAboutPage(manager, "about:devtools");
521        blockAboutPage(manager, "about:debugging");
522        blockAboutPage(manager, "about:devtools-toolbox");
523        blockAboutPage(manager, "about:profiling");
524      }
525    },
526  },
527
528  DisableFeedbackCommands: {
529    onBeforeUIStartup(manager, param) {
530      if (param) {
531        manager.disallowFeature("feedbackCommands");
532      }
533    },
534  },
535
536  DisableFirefoxAccounts: {
537    onBeforeAddons(manager, param) {
538      if (param) {
539        setAndLockPref("identity.fxaccounts.enabled", false);
540        setAndLockPref("trailhead.firstrun.branches", "nofirstrun-empty");
541        setAndLockPref("browser.aboutwelcome.enabled", false);
542      }
543    },
544  },
545
546  DisableFirefoxScreenshots: {
547    onBeforeAddons(manager, param) {
548      if (param) {
549        setAndLockPref("extensions.screenshots.disabled", true);
550      }
551    },
552  },
553
554  DisableFirefoxStudies: {
555    onBeforeAddons(manager, param) {
556      if (param) {
557        manager.disallowFeature("Shield");
558        setAndLockPref(
559          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
560          false
561        );
562        setAndLockPref(
563          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
564          false
565        );
566      }
567    },
568  },
569
570  DisableForgetButton: {
571    onProfileAfterChange(manager, param) {
572      if (param) {
573        setAndLockPref("privacy.panicButton.enabled", false);
574      }
575    },
576  },
577
578  DisableFormHistory: {
579    onBeforeUIStartup(manager, param) {
580      if (param) {
581        setAndLockPref("browser.formfill.enable", false);
582      }
583    },
584  },
585
586  DisableMasterPasswordCreation: {
587    onBeforeUIStartup(manager, param) {
588      if (param) {
589        manager.disallowFeature("createMasterPassword");
590      }
591    },
592  },
593
594  DisablePasswordReveal: {
595    onBeforeUIStartup(manager, param) {
596      if (param) {
597        manager.disallowFeature("passwordReveal");
598      }
599    },
600  },
601
602  DisablePocket: {
603    onBeforeAddons(manager, param) {
604      if (param) {
605        setAndLockPref("extensions.pocket.enabled", false);
606      }
607    },
608  },
609
610  DisablePrivateBrowsing: {
611    onBeforeAddons(manager, param) {
612      if (param) {
613        manager.disallowFeature("privatebrowsing");
614        blockAboutPage(manager, "about:privatebrowsing", true);
615        setAndLockPref("browser.privatebrowsing.autostart", false);
616      }
617    },
618  },
619
620  DisableProfileImport: {
621    onBeforeUIStartup(manager, param) {
622      if (param) {
623        manager.disallowFeature("profileImport");
624        setAndLockPref(
625          "browser.newtabpage.activity-stream.migrationExpired",
626          true
627        );
628      }
629    },
630  },
631
632  DisableProfileRefresh: {
633    onBeforeUIStartup(manager, param) {
634      if (param) {
635        manager.disallowFeature("profileRefresh");
636        setAndLockPref("browser.disableResetPrompt", true);
637      }
638    },
639  },
640
641  DisableSafeMode: {
642    onBeforeUIStartup(manager, param) {
643      if (param) {
644        manager.disallowFeature("safeMode");
645      }
646    },
647  },
648
649  DisableSecurityBypass: {
650    onBeforeUIStartup(manager, param) {
651      if ("InvalidCertificate" in param) {
652        setAndLockPref(
653          "security.certerror.hideAddException",
654          param.InvalidCertificate
655        );
656      }
657
658      if ("SafeBrowsing" in param) {
659        setAndLockPref(
660          "browser.safebrowsing.allowOverride",
661          !param.SafeBrowsing
662        );
663      }
664    },
665  },
666
667  DisableSetDesktopBackground: {
668    onBeforeUIStartup(manager, param) {
669      if (param) {
670        manager.disallowFeature("setDesktopBackground");
671      }
672    },
673  },
674
675  DisableSystemAddonUpdate: {
676    onBeforeAddons(manager, param) {
677      if (param) {
678        manager.disallowFeature("SysAddonUpdate");
679      }
680    },
681  },
682
683  DisableTelemetry: {
684    onBeforeAddons(manager, param) {
685      if (param) {
686        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
687        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
688        setAndLockPref("toolkit.telemetry.archive.enabled", false);
689        blockAboutPage(manager, "about:telemetry");
690      }
691    },
692  },
693
694  DisplayBookmarksToolbar: {
695    onBeforeUIStartup(manager, param) {
696      let value = (!param).toString();
697      // This policy is meant to change the default behavior, not to force it.
698      // If this policy was alreay applied and the user chose to re-hide the
699      // bookmarks toolbar, do not show it again.
700      runOncePerModification("displayBookmarksToolbar", value, () => {
701        gXulStore.setValue(
702          BROWSER_DOCUMENT_URL,
703          "PersonalToolbar",
704          "collapsed",
705          value
706        );
707      });
708    },
709  },
710
711  DisplayMenuBar: {
712    onBeforeUIStartup(manager, param) {
713      let value;
714      if (
715        typeof param === "boolean" ||
716        param == "default-on" ||
717        param == "default-off"
718      ) {
719        switch (param) {
720          case "default-on":
721            value = "false";
722            break;
723          case "default-off":
724            value = "true";
725            break;
726          default:
727            value = (!param).toString();
728            break;
729        }
730        // This policy is meant to change the default behavior, not to force it.
731        // If this policy was already applied and the user chose to re-hide the
732        // menu bar, do not show it again.
733        runOncePerModification("displayMenuBar", value, () => {
734          gXulStore.setValue(
735            BROWSER_DOCUMENT_URL,
736            "toolbar-menubar",
737            "autohide",
738            value
739          );
740        });
741      } else {
742        switch (param) {
743          case "always":
744            value = "false";
745            break;
746          case "never":
747            // Make sure Alt key doesn't show the menubar
748            setAndLockPref("ui.key.menuAccessKeyFocuses", false);
749            value = "true";
750            break;
751        }
752        gXulStore.setValue(
753          BROWSER_DOCUMENT_URL,
754          "toolbar-menubar",
755          "autohide",
756          value
757        );
758        manager.disallowFeature("hideShowMenuBar");
759      }
760    },
761  },
762
763  DNSOverHTTPS: {
764    onBeforeAddons(manager, param) {
765      let locked = false;
766      if ("Locked" in param) {
767        locked = param.Locked;
768      }
769      if ("Enabled" in param) {
770        let mode = param.Enabled ? 2 : 5;
771        setDefaultPref("network.trr.mode", mode, locked);
772      }
773      if ("ProviderURL" in param) {
774        setDefaultPref("network.trr.uri", param.ProviderURL.href, locked);
775      }
776      if ("ExcludedDomains" in param) {
777        setDefaultPref(
778          "network.trr.excluded-domains",
779          param.ExcludedDomains.join(","),
780          locked
781        );
782      }
783    },
784  },
785
786  DontCheckDefaultBrowser: {
787    onBeforeUIStartup(manager, param) {
788      setAndLockPref("browser.shell.checkDefaultBrowser", !param);
789    },
790  },
791
792  DownloadDirectory: {
793    onBeforeAddons(manager, param) {
794      setAndLockPref("browser.download.dir", replacePathVariables(param));
795      // If a custom download directory is being used, just lock folder list to 2.
796      setAndLockPref("browser.download.folderList", 2);
797      // Per Chrome spec, user can't choose to download every time
798      // if this is set.
799      setAndLockPref("browser.download.useDownloadDir", true);
800    },
801  },
802
803  EnableTrackingProtection: {
804    onBeforeUIStartup(manager, param) {
805      if (param.Value) {
806        setDefaultPref(
807          "privacy.trackingprotection.enabled",
808          true,
809          param.Locked
810        );
811        setDefaultPref(
812          "privacy.trackingprotection.pbmode.enabled",
813          true,
814          param.Locked
815        );
816      } else {
817        setAndLockPref("privacy.trackingprotection.enabled", false);
818        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
819      }
820      if ("Cryptomining" in param) {
821        setDefaultPref(
822          "privacy.trackingprotection.cryptomining.enabled",
823          param.Cryptomining,
824          param.Locked
825        );
826      }
827      if ("Fingerprinting" in param) {
828        setDefaultPref(
829          "privacy.trackingprotection.fingerprinting.enabled",
830          param.Fingerprinting,
831          param.Locked
832        );
833      }
834      if ("Exceptions" in param) {
835        addAllowDenyPermissions("trackingprotection", param.Exceptions);
836      }
837    },
838  },
839
840  EncryptedMediaExtensions: {
841    onBeforeAddons(manager, param) {
842      let locked = false;
843      if ("Locked" in param) {
844        locked = param.Locked;
845      }
846      if ("Enabled" in param) {
847        setDefaultPref("media.eme.enabled", param.Enabled, locked);
848      }
849    },
850  },
851
852  Extensions: {
853    onBeforeUIStartup(manager, param) {
854      let uninstallingPromise = Promise.resolve();
855      if ("Uninstall" in param) {
856        uninstallingPromise = runOncePerModification(
857          "extensionsUninstall",
858          JSON.stringify(param.Uninstall),
859          async () => {
860            // If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't
861            // changed, which will allow add-ons to be updated.
862            Services.prefs.clearUserPref(
863              "browser.policies.runOncePerModification.extensionsInstall"
864            );
865            let addons = await AddonManager.getAddonsByIDs(param.Uninstall);
866            for (let addon of addons) {
867              if (addon) {
868                try {
869                  await addon.uninstall();
870                } catch (e) {
871                  // This can fail for add-ons that can't be uninstalled.
872                  log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
873                }
874              }
875            }
876          }
877        );
878      }
879      if ("Install" in param) {
880        runOncePerModification(
881          "extensionsInstall",
882          JSON.stringify(param.Install),
883          async () => {
884            await uninstallingPromise;
885            for (let location of param.Install) {
886              let uri;
887              try {
888                // We need to try as a file first because
889                // Windows paths are valid URIs.
890                // This is done for legacy support (old API)
891                let xpiFile = new FileUtils.File(location);
892                uri = Services.io.newFileURI(xpiFile);
893              } catch (e) {
894                uri = Services.io.newURI(location);
895              }
896              installAddonFromURL(uri.spec);
897            }
898          }
899        );
900      }
901      if ("Locked" in param) {
902        for (let ID of param.Locked) {
903          manager.disallowFeature(`uninstall-extension:${ID}`);
904          manager.disallowFeature(`disable-extension:${ID}`);
905        }
906      }
907    },
908  },
909
910  ExtensionSettings: {
911    onBeforeAddons(manager, param) {
912      try {
913        manager.setExtensionSettings(param);
914      } catch (e) {
915        log.error("Invalid ExtensionSettings");
916      }
917    },
918    async onBeforeUIStartup(manager, param) {
919      let extensionSettings = param;
920      let blockAllExtensions = false;
921      if ("*" in extensionSettings) {
922        if (
923          "installation_mode" in extensionSettings["*"] &&
924          extensionSettings["*"].installation_mode == "blocked"
925        ) {
926          blockAllExtensions = true;
927          // Turn off discovery pane in about:addons
928          setAndLockPref("extensions.getAddons.showPane", false);
929          // Turn off recommendations
930          setAndLockPref(
931            "extensions.htmlaboutaddons.recommendations.enable",
932            false
933          );
934          // Block about:debugging
935          blockAboutPage(manager, "about:debugging");
936        }
937        if ("restricted_domains" in extensionSettings["*"]) {
938          let restrictedDomains = Services.prefs
939            .getCharPref("extensions.webextensions.restrictedDomains")
940            .split(",");
941          setAndLockPref(
942            "extensions.webextensions.restrictedDomains",
943            restrictedDomains
944              .concat(extensionSettings["*"].restricted_domains)
945              .join(",")
946          );
947        }
948      }
949      let addons = await AddonManager.getAllAddons();
950      let allowedExtensions = [];
951      for (let extensionID in extensionSettings) {
952        if (extensionID == "*") {
953          // Ignore global settings
954          continue;
955        }
956        if ("installation_mode" in extensionSettings[extensionID]) {
957          if (
958            extensionSettings[extensionID].installation_mode ==
959              "force_installed" ||
960            extensionSettings[extensionID].installation_mode ==
961              "normal_installed"
962          ) {
963            if (!extensionSettings[extensionID].install_url) {
964              throw new Error(`Missing install_url for ${extensionID}`);
965            }
966            installAddonFromURL(
967              extensionSettings[extensionID].install_url,
968              extensionID,
969              addons.find(addon => addon.id == extensionID)
970            );
971            manager.disallowFeature(`uninstall-extension:${extensionID}`);
972            if (
973              extensionSettings[extensionID].installation_mode ==
974              "force_installed"
975            ) {
976              manager.disallowFeature(`disable-extension:${extensionID}`);
977            }
978            allowedExtensions.push(extensionID);
979          } else if (
980            extensionSettings[extensionID].installation_mode == "allowed"
981          ) {
982            allowedExtensions.push(extensionID);
983          } else if (
984            extensionSettings[extensionID].installation_mode == "blocked"
985          ) {
986            if (addons.find(addon => addon.id == extensionID)) {
987              // Can't use the addon from getActiveAddons since it doesn't have uninstall.
988              let addon = await AddonManager.getAddonByID(extensionID);
989              try {
990                await addon.uninstall();
991              } catch (e) {
992                // This can fail for add-ons that can't be uninstalled.
993                log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
994              }
995            }
996          }
997        }
998      }
999      if (blockAllExtensions) {
1000        for (let addon of addons) {
1001          if (
1002            addon.isSystem ||
1003            addon.isBuiltin ||
1004            !(addon.scope & AddonManager.SCOPE_PROFILE)
1005          ) {
1006            continue;
1007          }
1008          if (!allowedExtensions.includes(addon.id)) {
1009            try {
1010              // Can't use the addon from getActiveAddons since it doesn't have uninstall.
1011              let addonToUninstall = await AddonManager.getAddonByID(addon.id);
1012              await addonToUninstall.uninstall();
1013            } catch (e) {
1014              // This can fail for add-ons that can't be uninstalled.
1015              log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
1016            }
1017          }
1018        }
1019      }
1020    },
1021  },
1022
1023  ExtensionUpdate: {
1024    onBeforeAddons(manager, param) {
1025      if (!param) {
1026        setAndLockPref("extensions.update.enabled", param);
1027      }
1028    },
1029  },
1030
1031  FirefoxHome: {
1032    onBeforeAddons(manager, param) {
1033      let locked = param.Locked || false;
1034      if ("Search" in param) {
1035        setDefaultPref(
1036          "browser.newtabpage.activity-stream.showSearch",
1037          param.Search,
1038          locked
1039        );
1040      }
1041      if ("TopSites" in param) {
1042        setDefaultPref(
1043          "browser.newtabpage.activity-stream.feeds.topsites",
1044          param.TopSites,
1045          locked
1046        );
1047      }
1048      if ("Highlights" in param) {
1049        setDefaultPref(
1050          "browser.newtabpage.activity-stream.feeds.section.highlights",
1051          param.Highlights,
1052          locked
1053        );
1054      }
1055      if ("Pocket" in param) {
1056        setDefaultPref(
1057          "browser.newtabpage.activity-stream.feeds.section.topstories",
1058          param.Pocket,
1059          locked
1060        );
1061      }
1062      if ("Snippets" in param) {
1063        setDefaultPref(
1064          "browser.newtabpage.activity-stream.feeds.snippets",
1065          param.Snippets,
1066          locked
1067        );
1068      }
1069    },
1070  },
1071
1072  FlashPlugin: {
1073    onBeforeUIStartup(manager, param) {
1074      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
1075
1076      const FLASH_NEVER_ACTIVATE = 0;
1077      const FLASH_ASK_TO_ACTIVATE = 1;
1078
1079      let flashPrefVal;
1080      if (param.Default === undefined || param.Default) {
1081        flashPrefVal = FLASH_ASK_TO_ACTIVATE;
1082      } else {
1083        flashPrefVal = FLASH_NEVER_ACTIVATE;
1084      }
1085      if (param.Locked) {
1086        setAndLockPref("plugin.state.flash", flashPrefVal);
1087      } else if (param.Default !== undefined) {
1088        setDefaultPref("plugin.state.flash", flashPrefVal);
1089      }
1090    },
1091  },
1092
1093  Handlers: {
1094    onBeforeAddons(manager, param) {
1095      if ("mimeTypes" in param) {
1096        for (let mimeType in param.mimeTypes) {
1097          let mimeInfo = param.mimeTypes[mimeType];
1098          let realMIMEInfo = gMIMEService.getFromTypeAndExtension(mimeType, "");
1099          processMIMEInfo(mimeInfo, realMIMEInfo);
1100        }
1101      }
1102      if ("extensions" in param) {
1103        for (let extension in param.extensions) {
1104          let mimeInfo = param.extensions[extension];
1105          try {
1106            let realMIMEInfo = gMIMEService.getFromTypeAndExtension(
1107              "",
1108              extension
1109            );
1110            processMIMEInfo(mimeInfo, realMIMEInfo);
1111          } catch (e) {
1112            log.error(`Invalid file extension (${extension})`);
1113          }
1114        }
1115      }
1116      if ("schemes" in param) {
1117        for (let scheme in param.schemes) {
1118          let handlerInfo = param.schemes[scheme];
1119          let realHandlerInfo = gExternalProtocolService.getProtocolHandlerInfo(
1120            scheme
1121          );
1122          processMIMEInfo(handlerInfo, realHandlerInfo);
1123        }
1124      }
1125    },
1126  },
1127
1128  HardwareAcceleration: {
1129    onBeforeAddons(manager, param) {
1130      if (!param) {
1131        setAndLockPref("layers.acceleration.disabled", true);
1132      }
1133    },
1134  },
1135
1136  Homepage: {
1137    onBeforeUIStartup(manager, param) {
1138      if ("StartPage" in param && param.StartPage == "none") {
1139        // For blank startpage, we use about:blank rather
1140        // than messing with browser.startup.page
1141        param.URL = new URL("about:blank");
1142      }
1143      // |homepages| will be a string containing a pipe-separated ('|') list of
1144      // URLs because that is what the "Home page" section of about:preferences
1145      // (and therefore what the pref |browser.startup.homepage|) accepts.
1146      if ("URL" in param) {
1147        let homepages = param.URL.href;
1148        if (param.Additional && param.Additional.length) {
1149          homepages += "|" + param.Additional.map(url => url.href).join("|");
1150        }
1151        setDefaultPref("browser.startup.homepage", homepages, param.Locked);
1152        if (param.Locked) {
1153          setAndLockPref(
1154            "pref.browser.homepage.disable_button.current_page",
1155            true
1156          );
1157          setAndLockPref(
1158            "pref.browser.homepage.disable_button.bookmark_page",
1159            true
1160          );
1161          setAndLockPref(
1162            "pref.browser.homepage.disable_button.restore_default",
1163            true
1164          );
1165        } else {
1166          // Clear out old run once modification that is no longer used.
1167          clearRunOnceModification("setHomepage");
1168        }
1169      }
1170      if (param.StartPage) {
1171        let prefValue;
1172        switch (param.StartPage) {
1173          case "homepage":
1174          case "homepage-locked":
1175          case "none":
1176            prefValue = 1;
1177            break;
1178          case "previous-session":
1179            prefValue = 3;
1180            break;
1181        }
1182        setDefaultPref(
1183          "browser.startup.page",
1184          prefValue,
1185          param.StartPage == "homepage-locked"
1186        );
1187      }
1188    },
1189  },
1190
1191  InstallAddonsPermission: {
1192    onBeforeUIStartup(manager, param) {
1193      if ("Allow" in param) {
1194        addAllowDenyPermissions("install", param.Allow, null);
1195      }
1196      if ("Default" in param) {
1197        setAndLockPref("xpinstall.enabled", param.Default);
1198        if (!param.Default) {
1199          blockAboutPage(manager, "about:debugging");
1200          setAndLockPref(
1201            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
1202            false
1203          );
1204          setAndLockPref(
1205            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
1206            false
1207          );
1208          manager.disallowFeature("xpinstall");
1209        }
1210      }
1211    },
1212  },
1213
1214  LegacyProfiles: {
1215    // Handled in nsToolkitProfileService.cpp (Windows only)
1216  },
1217
1218  LegacySameSiteCookieBehaviorEnabled: {
1219    onBeforeAddons(manager, param) {
1220      setDefaultPref("network.cookie.sameSite.laxByDefault", !param);
1221    },
1222  },
1223
1224  LegacySameSiteCookieBehaviorEnabledForDomainList: {
1225    onBeforeAddons(manager, param) {
1226      setDefaultPref(
1227        "network.cookie.sameSite.laxByDefault.disabledHosts",
1228        param.join(",")
1229      );
1230    },
1231  },
1232
1233  LocalFileLinks: {
1234    onBeforeAddons(manager, param) {
1235      // If there are existing capabilities, lock them with the policy pref.
1236      let policyNames = Services.prefs
1237        .getCharPref("capability.policy.policynames", "")
1238        .split(" ");
1239      policyNames.push("localfilelinks_policy");
1240      setAndLockPref("capability.policy.policynames", policyNames.join(" "));
1241      setAndLockPref(
1242        "capability.policy.localfilelinks_policy.checkloaduri.enabled",
1243        "allAccess"
1244      );
1245      setAndLockPref(
1246        "capability.policy.localfilelinks_policy.sites",
1247        param.join(" ")
1248      );
1249    },
1250  },
1251
1252  ManagedBookmarks: {},
1253
1254  NetworkPrediction: {
1255    onBeforeAddons(manager, param) {
1256      setAndLockPref("network.dns.disablePrefetch", !param);
1257      setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
1258    },
1259  },
1260
1261  NewTabPage: {
1262    onBeforeAddons(manager, param) {
1263      setAndLockPref("browser.newtabpage.enabled", param);
1264    },
1265  },
1266
1267  NoDefaultBookmarks: {
1268    onProfileAfterChange(manager, param) {
1269      if (param) {
1270        manager.disallowFeature("defaultBookmarks");
1271      }
1272    },
1273  },
1274
1275  OfferToSaveLogins: {
1276    onBeforeUIStartup(manager, param) {
1277      setAndLockPref("signon.rememberSignons", param);
1278    },
1279  },
1280
1281  OfferToSaveLoginsDefault: {
1282    onBeforeUIStartup(manager, param) {
1283      let policies = Services.policies.getActivePolicies();
1284      if ("OfferToSaveLogins" in policies) {
1285        log.error(
1286          `OfferToSaveLoginsDefault ignored because OfferToSaveLogins is present.`
1287        );
1288      } else {
1289        setDefaultPref("signon.rememberSignons", param);
1290      }
1291    },
1292  },
1293
1294  OverrideFirstRunPage: {
1295    onProfileAfterChange(manager, param) {
1296      let url = param ? param : "";
1297      setAndLockPref("startup.homepage_welcome_url", url);
1298      setAndLockPref("trailhead.firstrun.branches", "nofirstrun-empty");
1299      setAndLockPref("browser.aboutwelcome.enabled", false);
1300    },
1301  },
1302
1303  OverridePostUpdatePage: {
1304    onProfileAfterChange(manager, param) {
1305      let url = param ? param.href : "";
1306      setAndLockPref("startup.homepage_override_url", url);
1307      // The pref startup.homepage_override_url is only used
1308      // as a fallback when the update.xml file hasn't provided
1309      // a specific post-update URL.
1310      manager.disallowFeature("postUpdateCustomPage");
1311    },
1312  },
1313
1314  PasswordManagerEnabled: {
1315    onBeforeUIStartup(manager, param) {
1316      if (!param) {
1317        blockAboutPage(manager, "about:logins", true);
1318        setAndLockPref("pref.privacy.disable_button.view_passwords", true);
1319      }
1320      setAndLockPref("signon.rememberSignons", param);
1321    },
1322  },
1323
1324  PDFjs: {
1325    onBeforeAddons(manager, param) {
1326      if ("Enabled" in param) {
1327        setAndLockPref("pdfjs.disabled", !param.Enabled);
1328      }
1329      if ("EnablePermissions" in param) {
1330        setAndLockPref("pdfjs.enablePermissions", !param.Enabled);
1331      }
1332    },
1333  },
1334
1335  Permissions: {
1336    onBeforeUIStartup(manager, param) {
1337      if (param.Camera) {
1338        addAllowDenyPermissions(
1339          "camera",
1340          param.Camera.Allow,
1341          param.Camera.Block
1342        );
1343        setDefaultPermission("camera", param.Camera);
1344      }
1345
1346      if (param.Microphone) {
1347        addAllowDenyPermissions(
1348          "microphone",
1349          param.Microphone.Allow,
1350          param.Microphone.Block
1351        );
1352        setDefaultPermission("microphone", param.Microphone);
1353      }
1354
1355      if (param.Autoplay) {
1356        addAllowDenyPermissions(
1357          "autoplay-media",
1358          param.Autoplay.Allow,
1359          param.Autoplay.Block
1360        );
1361        if ("Default" in param.Autoplay) {
1362          let prefValue;
1363          switch (param.Autoplay.Default) {
1364            case "allow-audio-video":
1365              prefValue = 0;
1366              break;
1367            case "block-audio":
1368              prefValue = 1;
1369              break;
1370            case "block-audio-video":
1371              prefValue = 5;
1372              break;
1373          }
1374          setDefaultPref(
1375            "media.autoplay.default",
1376            prefValue,
1377            param.Autoplay.Locked
1378          );
1379        }
1380      }
1381
1382      if (param.Location) {
1383        addAllowDenyPermissions(
1384          "geo",
1385          param.Location.Allow,
1386          param.Location.Block
1387        );
1388        setDefaultPermission("geo", param.Location);
1389      }
1390
1391      if (param.Notifications) {
1392        addAllowDenyPermissions(
1393          "desktop-notification",
1394          param.Notifications.Allow,
1395          param.Notifications.Block
1396        );
1397        setDefaultPermission("desktop-notification", param.Notifications);
1398      }
1399
1400      if ("VirtualReality" in param) {
1401        addAllowDenyPermissions(
1402          "xr",
1403          param.VirtualReality.Allow,
1404          param.VirtualReality.Block
1405        );
1406        setDefaultPermission("xr", param.VirtualReality);
1407      }
1408    },
1409  },
1410
1411  PictureInPicture: {
1412    onBeforeAddons(manager, param) {
1413      if ("Enabled" in param) {
1414        setDefaultPref(
1415          "media.videocontrols.picture-in-picture.video-toggle.enabled",
1416          param.Enabled
1417        );
1418      }
1419      if (param.Locked) {
1420        Services.prefs.lockPref(
1421          "media.videocontrols.picture-in-picture.video-toggle.enabled"
1422        );
1423      }
1424    },
1425  },
1426
1427  PopupBlocking: {
1428    onBeforeUIStartup(manager, param) {
1429      addAllowDenyPermissions("popup", param.Allow, null);
1430
1431      if (param.Locked) {
1432        let blockValue = true;
1433        if (param.Default !== undefined && !param.Default) {
1434          blockValue = false;
1435        }
1436        setAndLockPref("dom.disable_open_during_load", blockValue);
1437      } else if (param.Default !== undefined) {
1438        setDefaultPref("dom.disable_open_during_load", !!param.Default);
1439      }
1440    },
1441  },
1442
1443  Preferences: {
1444    onBeforeAddons(manager, param) {
1445      const allowedPrefixes = [
1446        "accessibility.",
1447        "app.update.",
1448        "browser.",
1449        "datareporting.policy.",
1450        "dom.",
1451        "extensions.",
1452        "general.autoScroll",
1453        "general.smoothScroll",
1454        "geo.",
1455        "intl.",
1456        "layout.",
1457        "media.",
1458        "network.",
1459        "pdfjs.",
1460        "places.",
1461        "print.",
1462        "signon.",
1463        "spellchecker.",
1464        "ui.",
1465        "widget.",
1466      ];
1467      const allowedSecurityPrefs = [
1468        "security.default_personal_cert",
1469        "security.insecure_connection_text.enabled",
1470        "security.insecure_connection_text.pbmode.enabled",
1471        "security.insecure_field_warning.contextual.enabled",
1472        "security.mixed_content.block_active_content",
1473        "security.osclientcerts.autoload",
1474        "security.ssl.errorReporting.enabled",
1475        "security.tls.hello_downgrade_check",
1476        "security.tls.version.enable-deprecated",
1477        "security.warn_submit_secure_to_insecure",
1478      ];
1479      const blockedPrefs = [
1480        "app.update.channel",
1481        "app.update.lastUpdateTime",
1482        "app.update.migrated",
1483      ];
1484
1485      for (let preference in param) {
1486        if (blockedPrefs.includes(preference)) {
1487          log.error(
1488            `Unable to set preference ${preference}. Preference not allowed for security reasons.`
1489          );
1490          continue;
1491        }
1492        if (preference.startsWith("security.")) {
1493          if (!allowedSecurityPrefs.includes(preference)) {
1494            log.error(
1495              `Unable to set preference ${preference}. Preference not allowed for security reasons.`
1496            );
1497            continue;
1498          }
1499        } else if (
1500          !allowedPrefixes.some(prefix => preference.startsWith(prefix))
1501        ) {
1502          log.error(
1503            `Unable to set preference ${preference}. Preference not allowed for stability reasons.`
1504          );
1505          continue;
1506        }
1507        if (typeof param[preference] != "object") {
1508          // Legacy policy preferences
1509          setAndLockPref(preference, param[preference]);
1510        } else {
1511          if (param[preference].Status == "clear") {
1512            Services.prefs.clearUserPref(preference);
1513            continue;
1514          }
1515
1516          if (param[preference].Status == "user") {
1517            var prefBranch = Services.prefs;
1518          } else {
1519            prefBranch = Services.prefs.getDefaultBranch("");
1520          }
1521
1522          try {
1523            switch (typeof param[preference].Value) {
1524              case "boolean":
1525                prefBranch.setBoolPref(preference, param[preference].Value);
1526                break;
1527
1528              case "number":
1529                if (!Number.isInteger(param[preference].Value)) {
1530                  throw new Error(`Non-integer value for ${preference}`);
1531                }
1532
1533                // This is ugly, but necessary. On Windows GPO and macOS
1534                // configs, booleans are converted to 0/1. In the previous
1535                // Preferences implementation, the schema took care of
1536                // automatically converting these values to booleans.
1537                // Since we allow arbitrary prefs now, we have to do
1538                // something different. See bug 1666836.
1539                if (
1540                  prefBranch.getPrefType(preference) == prefBranch.PREF_INT ||
1541                  ![0, 1].includes(param[preference].Value)
1542                ) {
1543                  prefBranch.setIntPref(preference, param[preference].Value);
1544                } else {
1545                  prefBranch.setBoolPref(preference, !!param[preference].Value);
1546                }
1547                break;
1548
1549              case "string":
1550                prefBranch.setStringPref(preference, param[preference].Value);
1551                break;
1552            }
1553          } catch (e) {
1554            log.error(
1555              `Unable to set preference ${preference}. Probable type mismatch.`
1556            );
1557          }
1558
1559          if (param[preference].Status == "locked") {
1560            Services.prefs.lockPref(preference);
1561          }
1562        }
1563      }
1564    },
1565  },
1566
1567  PrimaryPassword: {
1568    onAllWindowsRestored(manager, param) {
1569      if (param) {
1570        manager.disallowFeature("removeMasterPassword");
1571      } else {
1572        manager.disallowFeature("createMasterPassword");
1573      }
1574    },
1575  },
1576
1577  PromptForDownloadLocation: {
1578    onBeforeAddons(manager, param) {
1579      setAndLockPref("browser.download.useDownloadDir", !param);
1580    },
1581  },
1582
1583  Proxy: {
1584    onBeforeAddons(manager, param) {
1585      if (param.Locked) {
1586        manager.disallowFeature("changeProxySettings");
1587        ProxyPolicies.configureProxySettings(param, setAndLockPref);
1588      } else {
1589        ProxyPolicies.configureProxySettings(param, setDefaultPref);
1590      }
1591    },
1592  },
1593
1594  RequestedLocales: {
1595    onBeforeAddons(manager, param) {
1596      let requestedLocales;
1597      if (Array.isArray(param)) {
1598        requestedLocales = param;
1599      } else if (param) {
1600        requestedLocales = param.split(",");
1601      } else {
1602        requestedLocales = [];
1603      }
1604      runOncePerModification(
1605        "requestedLocales",
1606        JSON.stringify(requestedLocales),
1607        () => {
1608          Services.locale.requestedLocales = requestedLocales;
1609        }
1610      );
1611    },
1612  },
1613
1614  SanitizeOnShutdown: {
1615    onBeforeUIStartup(manager, param) {
1616      if (typeof param === "boolean") {
1617        setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param);
1618        if (param) {
1619          setAndLockPref("privacy.clearOnShutdown.cache", true);
1620          setAndLockPref("privacy.clearOnShutdown.cookies", true);
1621          setAndLockPref("privacy.clearOnShutdown.downloads", true);
1622          setAndLockPref("privacy.clearOnShutdown.formdata", true);
1623          setAndLockPref("privacy.clearOnShutdown.history", true);
1624          setAndLockPref("privacy.clearOnShutdown.sessions", true);
1625          setAndLockPref("privacy.clearOnShutdown.siteSettings", true);
1626          setAndLockPref("privacy.clearOnShutdown.offlineApps", true);
1627        }
1628      } else {
1629        let locked = true;
1630        // Needed to preserve original behavior in perpetuity.
1631        let lockDefaultPrefs = true;
1632        if ("Locked" in param) {
1633          locked = param.Locked;
1634          lockDefaultPrefs = false;
1635        }
1636        setDefaultPref("privacy.sanitize.sanitizeOnShutdown", true, locked);
1637        if ("Cache" in param) {
1638          setDefaultPref("privacy.clearOnShutdown.cache", param.Cache, locked);
1639        } else {
1640          setDefaultPref(
1641            "privacy.clearOnShutdown.cache",
1642            false,
1643            lockDefaultPrefs
1644          );
1645        }
1646        if ("Cookies" in param) {
1647          setDefaultPref(
1648            "privacy.clearOnShutdown.cookies",
1649            param.Cookies,
1650            locked
1651          );
1652        } else {
1653          setDefaultPref(
1654            "privacy.clearOnShutdown.cookies",
1655            false,
1656            lockDefaultPrefs
1657          );
1658        }
1659        if ("Downloads" in param) {
1660          setDefaultPref(
1661            "privacy.clearOnShutdown.downloads",
1662            param.Downloads,
1663            locked
1664          );
1665        } else {
1666          setDefaultPref(
1667            "privacy.clearOnShutdown.downloads",
1668            false,
1669            lockDefaultPrefs
1670          );
1671        }
1672        if ("FormData" in param) {
1673          setDefaultPref(
1674            "privacy.clearOnShutdown.formdata",
1675            param.FormData,
1676            locked
1677          );
1678        } else {
1679          setDefaultPref(
1680            "privacy.clearOnShutdown.formdata",
1681            false,
1682            lockDefaultPrefs
1683          );
1684        }
1685        if ("History" in param) {
1686          setDefaultPref(
1687            "privacy.clearOnShutdown.history",
1688            param.History,
1689            locked
1690          );
1691        } else {
1692          setDefaultPref(
1693            "privacy.clearOnShutdown.history",
1694            false,
1695            lockDefaultPrefs
1696          );
1697        }
1698        if ("Sessions" in param) {
1699          setDefaultPref(
1700            "privacy.clearOnShutdown.sessions",
1701            param.Sessions,
1702            locked
1703          );
1704        } else {
1705          setDefaultPref(
1706            "privacy.clearOnShutdown.sessions",
1707            false,
1708            lockDefaultPrefs
1709          );
1710        }
1711        if ("SiteSettings" in param) {
1712          setDefaultPref(
1713            "privacy.clearOnShutdown.siteSettings",
1714            param.SiteSettings,
1715            locked
1716          );
1717        }
1718        if ("OfflineApps" in param) {
1719          setDefaultPref(
1720            "privacy.clearOnShutdown.offlineApps",
1721            param.OfflineApps,
1722            locked
1723          );
1724        }
1725      }
1726    },
1727  },
1728
1729  SearchBar: {
1730    onAllWindowsRestored(manager, param) {
1731      // This policy is meant to change the default behavior, not to force it.
1732      // If this policy was already applied and the user chose move the search
1733      // bar, don't move it again.
1734      runOncePerModification("searchInNavBar", param, () => {
1735        if (param == "separate") {
1736          CustomizableUI.addWidgetToArea(
1737            "search-container",
1738            CustomizableUI.AREA_NAVBAR,
1739            CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1
1740          );
1741        } else if (param == "unified") {
1742          CustomizableUI.removeWidgetFromArea("search-container");
1743        }
1744      });
1745    },
1746  },
1747
1748  SearchEngines: {
1749    onBeforeUIStartup(manager, param) {
1750      if (param.PreventInstalls) {
1751        manager.disallowFeature("installSearchEngine", true);
1752      }
1753    },
1754    onAllWindowsRestored(manager, param) {
1755      Services.search.init().then(async () => {
1756        if (param.Remove) {
1757          // Only rerun if the list of engine names has changed.
1758          await runOncePerModification(
1759            "removeSearchEngines",
1760            JSON.stringify(param.Remove),
1761            async function() {
1762              for (let engineName of param.Remove) {
1763                let engine = Services.search.getEngineByName(engineName);
1764                if (engine) {
1765                  try {
1766                    await Services.search.removeEngine(engine);
1767                  } catch (ex) {
1768                    log.error("Unable to remove the search engine", ex);
1769                  }
1770                }
1771              }
1772            }
1773          );
1774        }
1775        if (param.Add) {
1776          // Only rerun if the list of engine names has changed.
1777          let engineNameList = param.Add.map(engine => engine.Name);
1778          await runOncePerModification(
1779            "addSearchEngines",
1780            JSON.stringify(engineNameList),
1781            async function() {
1782              for (let newEngine of param.Add) {
1783                let newEngineParameters = {
1784                  template: newEngine.URLTemplate,
1785                  iconURL: newEngine.IconURL ? newEngine.IconURL.href : null,
1786                  alias: newEngine.Alias,
1787                  description: newEngine.Description,
1788                  method: newEngine.Method,
1789                  postData: newEngine.PostData,
1790                  suggestURL: newEngine.SuggestURLTemplate,
1791                  extensionID: "set-via-policy",
1792                  queryCharset: "UTF-8",
1793                };
1794                try {
1795                  await Services.search.addEngineWithDetails(
1796                    newEngine.Name,
1797                    newEngineParameters
1798                  );
1799                } catch (ex) {
1800                  log.error("Unable to add search engine", ex);
1801                }
1802              }
1803            }
1804          );
1805        }
1806        if (param.Default) {
1807          await runOncePerModification(
1808            "setDefaultSearchEngine",
1809            param.Default,
1810            async () => {
1811              let defaultEngine;
1812              try {
1813                defaultEngine = Services.search.getEngineByName(param.Default);
1814                if (!defaultEngine) {
1815                  throw new Error("No engine by that name could be found");
1816                }
1817              } catch (ex) {
1818                log.error(
1819                  `Search engine lookup failed when attempting to set ` +
1820                    `the default engine. Requested engine was ` +
1821                    `"${param.Default}".`,
1822                  ex
1823                );
1824              }
1825              if (defaultEngine) {
1826                try {
1827                  await Services.search.setDefault(defaultEngine);
1828                } catch (ex) {
1829                  log.error("Unable to set the default search engine", ex);
1830                }
1831              }
1832            }
1833          );
1834        }
1835        if (param.DefaultPrivate) {
1836          await runOncePerModification(
1837            "setDefaultPrivateSearchEngine",
1838            param.DefaultPrivate,
1839            async () => {
1840              let defaultPrivateEngine;
1841              try {
1842                defaultPrivateEngine = Services.search.getEngineByName(
1843                  param.DefaultPrivate
1844                );
1845                if (!defaultPrivateEngine) {
1846                  throw new Error("No engine by that name could be found");
1847                }
1848              } catch (ex) {
1849                log.error(
1850                  `Search engine lookup failed when attempting to set ` +
1851                    `the default private engine. Requested engine was ` +
1852                    `"${param.DefaultPrivate}".`,
1853                  ex
1854                );
1855              }
1856              if (defaultPrivateEngine) {
1857                try {
1858                  await Services.search.setDefaultPrivate(defaultPrivateEngine);
1859                } catch (ex) {
1860                  log.error(
1861                    "Unable to set the default private search engine",
1862                    ex
1863                  );
1864                }
1865              }
1866            }
1867          );
1868        }
1869      });
1870    },
1871  },
1872
1873  SearchSuggestEnabled: {
1874    onBeforeAddons(manager, param) {
1875      setAndLockPref("browser.urlbar.suggest.searches", param);
1876      setAndLockPref("browser.search.suggest.enabled", param);
1877    },
1878  },
1879
1880  SecurityDevices: {
1881    onProfileAfterChange(manager, param) {
1882      let securityDevices = param;
1883      let pkcs11db = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
1884        Ci.nsIPKCS11ModuleDB
1885      );
1886      let moduleList = pkcs11db.listModules();
1887      for (let deviceName in securityDevices) {
1888        let foundModule = false;
1889        for (let module of moduleList) {
1890          if (module && module.libName === securityDevices[deviceName]) {
1891            foundModule = true;
1892            break;
1893          }
1894        }
1895        if (foundModule) {
1896          continue;
1897        }
1898        try {
1899          pkcs11db.addModule(deviceName, securityDevices[deviceName], 0, 0);
1900        } catch (ex) {
1901          log.error(`Unable to add security device ${deviceName}`);
1902          log.debug(ex);
1903        }
1904      }
1905    },
1906  },
1907
1908  SSLVersionMax: {
1909    onBeforeAddons(manager, param) {
1910      let tlsVersion;
1911      switch (param) {
1912        case "tls1":
1913          tlsVersion = 1;
1914          break;
1915        case "tls1.1":
1916          tlsVersion = 2;
1917          break;
1918        case "tls1.2":
1919          tlsVersion = 3;
1920          break;
1921        case "tls1.3":
1922          tlsVersion = 4;
1923          break;
1924      }
1925      setAndLockPref("security.tls.version.max", tlsVersion);
1926    },
1927  },
1928
1929  SSLVersionMin: {
1930    onBeforeAddons(manager, param) {
1931      let tlsVersion;
1932      switch (param) {
1933        case "tls1":
1934          tlsVersion = 1;
1935          break;
1936        case "tls1.1":
1937          tlsVersion = 2;
1938          break;
1939        case "tls1.2":
1940          tlsVersion = 3;
1941          break;
1942        case "tls1.3":
1943          tlsVersion = 4;
1944          break;
1945      }
1946      setAndLockPref("security.tls.version.min", tlsVersion);
1947    },
1948  },
1949
1950  SupportMenu: {
1951    onProfileAfterChange(manager, param) {
1952      manager.setSupportMenu(param);
1953    },
1954  },
1955
1956  UserMessaging: {
1957    onBeforeAddons(manager, param) {
1958      let locked = false;
1959      if ("Locked" in param) {
1960        locked = param.Locked;
1961      }
1962      if ("WhatsNew" in param) {
1963        setDefaultPref(
1964          "browser.messaging-system.whatsNewPanel.enabled",
1965          param.WhatsNew,
1966          locked
1967        );
1968      }
1969      if ("ExtensionRecommendations" in param) {
1970        setDefaultPref(
1971          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
1972          param.ExtensionRecommendations,
1973          locked
1974        );
1975      }
1976      if ("FeatureRecommendations" in param) {
1977        setDefaultPref(
1978          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
1979          param.FeatureRecommendations,
1980          locked
1981        );
1982      }
1983      if ("UrlbarInterventions" in param && !param.UrlbarInterventions) {
1984        manager.disallowFeature("urlbarinterventions");
1985      }
1986      if ("SkipOnboarding") {
1987        setAndLockPref("trailhead.firstrun.branches", "nofirstrun-empty");
1988        setAndLockPref("browser.aboutwelcome.enabled", false);
1989      }
1990    },
1991  },
1992
1993  WebsiteFilter: {
1994    onBeforeUIStartup(manager, param) {
1995      WebsiteFilter.init(param.Block || [], param.Exceptions || []);
1996    },
1997  },
1998};
1999
2000/*
2001 * ====================
2002 * = HELPER FUNCTIONS =
2003 * ====================
2004 *
2005 * The functions below are helpers to be used by several policies.
2006 */
2007
2008/**
2009 * setAndLockPref
2010 *
2011 * Sets the _default_ value of a pref, and locks it (meaning that
2012 * the default value will always be returned, independent from what
2013 * is stored as the user value).
2014 * The value is only changed in memory, and not stored to disk.
2015 *
2016 * @param {string} prefName
2017 *        The pref to be changed
2018 * @param {boolean,number,string} prefValue
2019 *        The value to set and lock
2020 */
2021function setAndLockPref(prefName, prefValue) {
2022  setDefaultPref(prefName, prefValue, true);
2023}
2024
2025/**
2026 * setDefaultPref
2027 *
2028 * Sets the _default_ value of a pref and optionally locks it.
2029 * The value is only changed in memory, and not stored to disk.
2030 *
2031 * @param {string} prefName
2032 *        The pref to be changed
2033 * @param {boolean,number,string} prefValue
2034 *        The value to set
2035 * @param {boolean} locked
2036 *        Optionally lock the pref
2037 */
2038function setDefaultPref(prefName, prefValue, locked = false) {
2039  if (Services.prefs.prefIsLocked(prefName)) {
2040    Services.prefs.unlockPref(prefName);
2041  }
2042
2043  let defaults = Services.prefs.getDefaultBranch("");
2044
2045  switch (typeof prefValue) {
2046    case "boolean":
2047      defaults.setBoolPref(prefName, prefValue);
2048      break;
2049
2050    case "number":
2051      if (!Number.isInteger(prefValue)) {
2052        throw new Error(`Non-integer value for ${prefName}`);
2053      }
2054
2055      // This is ugly, but necessary. On Windows GPO and macOS
2056      // configs, booleans are converted to 0/1. In the previous
2057      // Preferences implementation, the schema took care of
2058      // automatically converting these values to booleans.
2059      // Since we allow arbitrary prefs now, we have to do
2060      // something different. See bug 1666836.
2061      if (
2062        defaults.getPrefType(prefName) == defaults.PREF_INT ||
2063        ![0, 1].includes(prefValue)
2064      ) {
2065        defaults.setIntPref(prefName, prefValue);
2066      } else {
2067        defaults.setBoolPref(prefName, !!prefValue);
2068      }
2069      break;
2070
2071    case "string":
2072      defaults.setStringPref(prefName, prefValue);
2073      break;
2074  }
2075
2076  if (locked) {
2077    Services.prefs.lockPref(prefName);
2078  }
2079}
2080
2081/**
2082 * setDefaultPermission
2083 *
2084 * Helper function to set preferences appropriately for the policy
2085 *
2086 * @param {string} policyName
2087 *        The name of the policy to set
2088 * @param {object} policyParam
2089 *        The object containing param for the policy
2090 */
2091function setDefaultPermission(policyName, policyParam) {
2092  if ("BlockNewRequests" in policyParam) {
2093    let prefName = "permissions.default." + policyName;
2094
2095    if (policyParam.BlockNewRequests) {
2096      setDefaultPref(prefName, 2, policyParam.Locked);
2097    } else {
2098      setDefaultPref(prefName, 0, policyParam.Locked);
2099    }
2100  }
2101}
2102
2103/**
2104 * addAllowDenyPermissions
2105 *
2106 * Helper function to call the permissions manager (Services.perms.addFromPrincipal)
2107 * for two arrays of URLs.
2108 *
2109 * @param {string} permissionName
2110 *        The name of the permission to change
2111 * @param {array} allowList
2112 *        The list of URLs to be set as ALLOW_ACTION for the chosen permission.
2113 * @param {array} blockList
2114 *        The list of URLs to be set as DENY_ACTION for the chosen permission.
2115 */
2116function addAllowDenyPermissions(permissionName, allowList, blockList) {
2117  allowList = allowList || [];
2118  blockList = blockList || [];
2119
2120  for (let origin of allowList) {
2121    try {
2122      Services.perms.addFromPrincipal(
2123        Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin),
2124        permissionName,
2125        Ci.nsIPermissionManager.ALLOW_ACTION,
2126        Ci.nsIPermissionManager.EXPIRE_POLICY
2127      );
2128    } catch (ex) {
2129      log.error(`Added by default for ${permissionName} permission in the permission
2130      manager - ${origin.href}`);
2131    }
2132  }
2133
2134  for (let origin of blockList) {
2135    Services.perms.addFromPrincipal(
2136      Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin),
2137      permissionName,
2138      Ci.nsIPermissionManager.DENY_ACTION,
2139      Ci.nsIPermissionManager.EXPIRE_POLICY
2140    );
2141  }
2142}
2143
2144/**
2145 * runOnce
2146 *
2147 * Helper function to run a callback only once per policy.
2148 *
2149 * @param {string} actionName
2150 *        A given name which will be used to track if this callback has run.
2151 * @param {Functon} callback
2152 *        The callback to run only once.
2153 */
2154// eslint-disable-next-line no-unused-vars
2155function runOnce(actionName, callback) {
2156  let prefName = `browser.policies.runonce.${actionName}`;
2157  if (Services.prefs.getBoolPref(prefName, false)) {
2158    log.debug(
2159      `Not running action ${actionName} again because it has already run.`
2160    );
2161    return;
2162  }
2163  Services.prefs.setBoolPref(prefName, true);
2164  callback();
2165}
2166
2167/**
2168 * runOncePerModification
2169 *
2170 * Helper function similar to runOnce. The difference is that runOnce runs the
2171 * callback once when the policy is set, then never again.
2172 * runOncePerModification runs the callback once each time the policy value
2173 * changes from its previous value.
2174 * If the callback that was passed is an async function, you can await on this
2175 * function to await for the callback.
2176 *
2177 * @param {string} actionName
2178 *        A given name which will be used to track if this callback has run.
2179 *        This string will be part of a pref name.
2180 * @param {string} policyValue
2181 *        The current value of the policy. This will be compared to previous
2182 *        values given to this function to determine if the policy value has
2183 *        changed. Regardless of the data type of the policy, this must be a
2184 *        string.
2185 * @param {Function} callback
2186 *        The callback to be run when the pref value changes
2187 * @returns Promise
2188 *        A promise that will resolve once the callback finishes running.
2189 *
2190 */
2191async function runOncePerModification(actionName, policyValue, callback) {
2192  let prefName = `browser.policies.runOncePerModification.${actionName}`;
2193  let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
2194  if (policyValue === oldPolicyValue) {
2195    log.debug(
2196      `Not running action ${actionName} again because the policy's value is unchanged`
2197    );
2198    return Promise.resolve();
2199  }
2200  Services.prefs.setStringPref(prefName, policyValue);
2201  return callback();
2202}
2203
2204/**
2205 * clearRunOnceModification
2206 *
2207 * Helper function that clears a runOnce policy.
2208 */
2209function clearRunOnceModification(actionName) {
2210  let prefName = `browser.policies.runOncePerModification.${actionName}`;
2211  Services.prefs.clearUserPref(prefName);
2212}
2213
2214function replacePathVariables(path) {
2215  if (path.includes("${home}")) {
2216    return path.replace("${home}", FileUtils.getFile("Home", []).path);
2217  }
2218  return path;
2219}
2220
2221/**
2222 * installAddonFromURL
2223 *
2224 * Helper function that installs an addon from a URL
2225 * and verifies that the addon ID matches.
2226 */
2227function installAddonFromURL(url, extensionID, addon) {
2228  if (
2229    addon &&
2230    addon.sourceURI &&
2231    addon.sourceURI.spec == url &&
2232    !addon.sourceURI.schemeIs("file")
2233  ) {
2234    // It's the same addon, don't reinstall.
2235    return;
2236  }
2237  AddonManager.getInstallForURL(url, {
2238    telemetryInfo: { source: "enterprise-policy" },
2239  }).then(install => {
2240    if (install.addon && install.addon.appDisabled) {
2241      log.error(`Incompatible add-on - ${location}`);
2242      install.cancel();
2243      return;
2244    }
2245    let listener = {
2246      /* eslint-disable-next-line no-shadow */
2247      onDownloadEnded: install => {
2248        if (extensionID && install.addon.id != extensionID) {
2249          log.error(
2250            `Add-on downloaded from ${url} had unexpected id (got ${install.addon.id} expected ${extensionID})`
2251          );
2252          install.removeListener(listener);
2253          install.cancel();
2254        }
2255        if (install.addon && install.addon.appDisabled) {
2256          log.error(`Incompatible add-on - ${url}`);
2257          install.removeListener(listener);
2258          install.cancel();
2259        }
2260        if (
2261          addon &&
2262          Services.vc.compare(addon.version, install.addon.version) == 0
2263        ) {
2264          log.debug("Installation cancelled because versions are the same");
2265          install.removeListener(listener);
2266          install.cancel();
2267        }
2268      },
2269      onDownloadFailed: () => {
2270        install.removeListener(listener);
2271        log.error(
2272          `Download failed - ${AddonManager.errorToString(
2273            install.error
2274          )} - ${url}`
2275        );
2276        clearRunOnceModification("extensionsInstall");
2277      },
2278      onInstallFailed: () => {
2279        install.removeListener(listener);
2280        log.error(
2281          `Installation failed - ${AddonManager.errorToString(
2282            install.error
2283          )} - {url}`
2284        );
2285      },
2286      /* eslint-disable-next-line no-shadow */
2287      onInstallEnded: (install, addon) => {
2288        if (addon.type == "theme") {
2289          addon.enable();
2290        }
2291        install.removeListener(listener);
2292        log.debug(`Installation succeeded - ${url}`);
2293      },
2294    };
2295    install.addListener(listener);
2296    install.install();
2297  });
2298}
2299
2300let gBlockedChromePages = [];
2301
2302function blockAboutPage(manager, feature, neededOnContentProcess = false) {
2303  if (!gBlockedChromePages.length) {
2304    addChromeURLBlocker();
2305  }
2306  manager.disallowFeature(feature, neededOnContentProcess);
2307  let splitURL = Services.io
2308    .newChannelFromURI(
2309      Services.io.newURI(feature),
2310      null,
2311      Services.scriptSecurityManager.getSystemPrincipal(),
2312      null,
2313      0,
2314      Ci.nsIContentPolicy.TYPE_OTHER
2315    )
2316    .URI.spec.split("/");
2317  // about:debugging uses index.html for a filename, so we need to rely
2318  // on more than just the filename.
2319  let fileName =
2320    splitURL[splitURL.length - 2] + "/" + splitURL[splitURL.length - 1];
2321  gBlockedChromePages.push(fileName);
2322  if (feature == "about:config") {
2323    // Hide old page until it is removed
2324    gBlockedChromePages.push("config.xhtml");
2325  }
2326}
2327
2328let ChromeURLBlockPolicy = {
2329  shouldLoad(contentLocation, loadInfo, mimeTypeGuess) {
2330    let contentType = loadInfo.externalContentPolicyType;
2331    if (
2332      contentLocation.scheme == "chrome" &&
2333      contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT &&
2334      loadInfo.loadingContext &&
2335      loadInfo.loadingContext.baseURI == AppConstants.BROWSER_CHROME_URL &&
2336      gBlockedChromePages.some(function(fileName) {
2337        return contentLocation.filePath.endsWith(fileName);
2338      })
2339    ) {
2340      return Ci.nsIContentPolicy.REJECT_REQUEST;
2341    }
2342    return Ci.nsIContentPolicy.ACCEPT;
2343  },
2344  shouldProcess(contentLocation, loadInfo, mimeTypeGuess) {
2345    return Ci.nsIContentPolicy.ACCEPT;
2346  },
2347  classDescription: "Policy Engine Content Policy",
2348  contractID: "@mozilla-org/policy-engine-content-policy-service;1",
2349  classID: Components.ID("{ba7b9118-cabc-4845-8b26-4215d2a59ed7}"),
2350  QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPolicy]),
2351  createInstance(outer, iid) {
2352    return this.QueryInterface(iid);
2353  },
2354};
2355
2356function addChromeURLBlocker() {
2357  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
2358  registrar.registerFactory(
2359    ChromeURLBlockPolicy.classID,
2360    ChromeURLBlockPolicy.classDescription,
2361    ChromeURLBlockPolicy.contractID,
2362    ChromeURLBlockPolicy
2363  );
2364
2365  Services.catMan.addCategoryEntry(
2366    "content-policy",
2367    ChromeURLBlockPolicy.contractID,
2368    ChromeURLBlockPolicy.contractID,
2369    false,
2370    true
2371  );
2372}
2373
2374function pemToBase64(pem) {
2375  return pem
2376    .replace(/(.*)-----BEGIN CERTIFICATE-----/, "")
2377    .replace(/-----END CERTIFICATE-----(.*)/, "")
2378    .replace(/[\r\n]/g, "");
2379}
2380
2381function processMIMEInfo(mimeInfo, realMIMEInfo) {
2382  if ("handlers" in mimeInfo) {
2383    let firstHandler = true;
2384    for (let handler of mimeInfo.handlers) {
2385      // handler can be null which means they don't
2386      // want a preferred handler.
2387      if (handler) {
2388        let handlerApp;
2389        if ("path" in handler) {
2390          try {
2391            let file = new FileUtils.File(handler.path);
2392            handlerApp = Cc[
2393              "@mozilla.org/uriloader/local-handler-app;1"
2394            ].createInstance(Ci.nsILocalHandlerApp);
2395            handlerApp.executable = file;
2396          } catch (ex) {
2397            log.error(`Unable to create handler executable (${handler.path})`);
2398            continue;
2399          }
2400        } else if ("uriTemplate" in handler) {
2401          let templateURL = new URL(handler.uriTemplate);
2402          if (templateURL.protocol != "https:") {
2403            log.error(`Web handler must be https (${handler.uriTemplate})`);
2404            continue;
2405          }
2406          if (
2407            !templateURL.pathname.includes("%s") &&
2408            !templateURL.search.includes("%s")
2409          ) {
2410            log.error(`Web handler must contain %s (${handler.uriTemplate})`);
2411            continue;
2412          }
2413          handlerApp = Cc[
2414            "@mozilla.org/uriloader/web-handler-app;1"
2415          ].createInstance(Ci.nsIWebHandlerApp);
2416          handlerApp.uriTemplate = handler.uriTemplate;
2417        } else {
2418          log.error("Invalid handler");
2419          continue;
2420        }
2421        if ("name" in handler) {
2422          handlerApp.name = handler.name;
2423        }
2424        realMIMEInfo.possibleApplicationHandlers.appendElement(handlerApp);
2425        if (firstHandler) {
2426          realMIMEInfo.preferredApplicationHandler = handlerApp;
2427        }
2428      }
2429      firstHandler = false;
2430    }
2431  }
2432  if ("action" in mimeInfo) {
2433    let action = realMIMEInfo[mimeInfo.action];
2434    if (
2435      action == realMIMEInfo.useHelperApp &&
2436      !realMIMEInfo.possibleApplicationHandlers.length
2437    ) {
2438      log.error("useHelperApp requires a handler");
2439      return;
2440    }
2441    realMIMEInfo.preferredAction = action;
2442  }
2443  if ("ask" in mimeInfo) {
2444    realMIMEInfo.alwaysAskBeforeHandling = mimeInfo.ask;
2445  }
2446  gHandlerService.store(realMIMEInfo);
2447}
2448