Policies.jsm 49.5 KB
Newer Older
1
2
3
4
5
6
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

7
8
9
10
11
12
13
const { XPCOMUtils } = ChromeUtils.import(
  "resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { AppConstants } = ChromeUtils.import(
  "resource://gre/modules/AppConstants.jsm"
);
14
15
16
17
18

XPCOMUtils.defineLazyServiceGetters(this, {
  gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
  gXulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"],
});
19

20
XPCOMUtils.defineLazyModuleGetters(this, {
21
  AddonManager: "resource://gre/modules/AddonManager.jsm",
22
  BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
23
  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
24
  FileUtils: "resource://gre/modules/FileUtils.jsm",
25
26
  ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
  WebsiteFilter: "resource:///modules/policies/WebsiteFilter.jsm",
27
28
});

29
30
XPCOMUtils.defineLazyGlobalGetters(this, ["File", "FileReader"]);

31
32
const PREF_LOGLEVEL = "browser.policies.loglevel";
const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL;
33
34

XPCOMUtils.defineLazyGetter(this, "log", () => {
35
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
36
37
38
39
40
41
42
43
44
  return new ConsoleAPI({
    prefix: "Policies.jsm",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

45
var EXPORTED_SYMBOLS = ["Policies"];
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
 * ============================
 * = POLICIES IMPLEMENTATIONS =
 * ============================
 *
 * The Policies object below is where the implementation for each policy
 * happens. An object for each policy should be defined, containing
 * callback functions that will be called by the engine.
 *
 * See the _callbacks object in EnterprisePolicies.js for the list of
 * possible callbacks and an explanation of each.
 *
 * Each callback will be called with two parameters:
 * - manager
 *   This is the EnterprisePoliciesManager singleton object from
 *   EnterprisePolicies.js
 *
 * - param
 *   The parameter defined for this policy in policies-schema.json.
 *   It will be different for each policy. It could be a boolean,
 *   a string, an array or a complex object. All parameters have
 *   been validated according to the schema, and no unknown
 *   properties will be present on them.
 *
 * The callbacks will be bound to their parent policy object.
 */
73
var Policies = {
74
75
76
77
78
79
  "3rdparty": {
    onBeforeAddons(manager, param) {
      manager.setExtensionPolicies(param.Extensions);
    },
  },

80
  AppUpdateURL: {
81
82
    onBeforeAddons(manager, param) {
      setDefaultPref("app.update.url", param.href);
83
    },
84
85
  },

86
  Authentication: {
87
88
    onBeforeAddons(manager, param) {
      if ("SPNEGO" in param) {
89
90
91
92
        setAndLockPref(
          "network.negotiate-auth.trusted-uris",
          param.SPNEGO.join(", ")
        );
93
94
      }
      if ("Delegated" in param) {
95
96
97
98
        setAndLockPref(
          "network.negotiate-auth.delegation-uris",
          param.Delegated.join(", ")
        );
99
100
      }
      if ("NTLM" in param) {
101
102
103
104
        setAndLockPref(
          "network.automatic-ntlm-auth.trusted-uris",
          param.NTLM.join(", ")
        );
105
      }
106
107
      if ("AllowNonFQDN" in param) {
        if (param.AllowNonFQDN.NTLM) {
108
109
110
111
          setAndLockPref(
            "network.automatic-ntlm-auth.allow-non-fqdn",
            param.AllowNonFQDN.NTLM
          );
112
113
        }
        if (param.AllowNonFQDN.SPNEGO) {
114
115
116
117
          setAndLockPref(
            "network.negotiate-auth.allow-non-fqdn",
            param.AllowNonFQDN.SPNEGO
          );
118
119
        }
      }
120
    },
121
122
  },

123
  BlockAboutAddons: {
124
125
    onBeforeUIStartup(manager, param) {
      if (param) {
126
        blockAboutPage(manager, "about:addons", true);
127
      }
128
    },
129
130
  },

131
  BlockAboutConfig: {
132
    onBeforeUIStartup(manager, param) {
133
      if (param) {
134
        blockAboutPage(manager, "about:config");
135
        setAndLockPref("devtools.chrome.enabled", false);
136
      }
137
    },
138
  },
139

140
  BlockAboutProfiles: {
141
142
    onBeforeUIStartup(manager, param) {
      if (param) {
143
        blockAboutPage(manager, "about:profiles");
144
      }
145
    },
146
147
  },

148
  BlockAboutSupport: {
149
150
    onBeforeUIStartup(manager, param) {
      if (param) {
151
        blockAboutPage(manager, "about:support");
152
      }
153
    },
154
155
  },

156
  Bookmarks: {
157
158
    onAllWindowsRestored(manager, param) {
      BookmarksPolicies.processBookmarks(param);
159
    },
160
161
  },

162
  CaptivePortal: {
163
164
165
166
167
    onBeforeAddons(manager, param) {
      setAndLockPref("network.captive-portal-service.enabled", param);
    },
  },

168
  Certificates: {
169
170
    onBeforeAddons(manager, param) {
      if ("ImportEnterpriseRoots" in param) {
171
172
173
174
        setAndLockPref(
          "security.enterprise_roots.enabled",
          param.ImportEnterpriseRoots
        );
175
      }
176
177
178
179
180
181
      if ("Install" in param) {
        (async () => {
          let dirs = [];
          let platform = AppConstants.platform;
          if (platform == "win") {
            dirs = [
182
              // Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
183
              Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
184
185
              // Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
              Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
186
187
188
189
190
191
192
193
            ];
          } else if (platform == "macosx" || platform == "linux") {
            dirs = [
              // These two keys are named wrong. They return the Mozilla directory.
              Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile),
              Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile),
            ];
          }
194
195
196
197
          dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile));
          for (let certfilename of param.Install) {
            let certfile;
            try {
198
199
200
              certfile = Cc["@mozilla.org/file/local;1"].createInstance(
                Ci.nsIFile
              );
201
202
203
204
              certfile.initWithPath(certfilename);
            } catch (e) {
              for (let dir of dirs) {
                certfile = dir.clone();
205
206
207
                certfile.append(
                  platform == "linux" ? "certificates" : "Certificates"
                );
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
                certfile.append(certfilename);
                if (certfile.exists()) {
                  break;
                }
              }
            }
            let file;
            try {
              file = await File.createFromNsIFile(certfile);
            } catch (e) {
              log.error(`Unable to find certificate - ${certfilename}`);
              continue;
            }
            let reader = new FileReader();
            reader.onloadend = function() {
              if (reader.readyState != reader.DONE) {
                log.error(`Unable to read certificate - ${certfile.path}`);
                return;
              }
227
228
              let certFile = reader.result;
              let cert;
229
              try {
230
                cert = gCertDB.constructX509(certFile);
231
              } catch (e) {
232
233
234
235
236
237
238
239
240
                try {
                  // It might be PEM instead of DER.
                  cert = gCertDB.constructX509FromBase64(pemToBase64(certFile));
                } catch (ex) {
                  log.error(`Unable to add certificate - ${certfile.path}`);
                }
              }
              let now = Date.now() / 1000;
              if (cert) {
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
                gCertDB.asyncVerifyCertAtTime(
                  cert,
                  0x0008 /* certificateUsageSSLCA */,
                  0,
                  null,
                  now,
                  (aPRErrorCode, aVerifiedChain, aHasEVPolicy) => {
                    if (aPRErrorCode == Cr.NS_OK) {
                      // Certificate is already installed.
                      return;
                    }
                    try {
                      gCertDB.addCert(certFile, "CT,CT,");
                    } catch (e) {
                      // It might be PEM instead of DER.
                      gCertDB.addCertFromBase64(
                        pemToBase64(certFile),
                        "CT,CT,"
                      );
                    }
261
                  }
262
                );
263
              }
264
265
            };
            reader.readAsBinaryString(file);
266
267
268
          }
        })();
      }
269
    },
270
271
  },

272
  Cookies: {
273
    onBeforeUIStartup(manager, param) {
274
      addAllowDenyPermissions("cookie", param.Allow, param.Block);
275
276

      if (param.Block) {
277
278
279
        const hosts = param.Block.map(url => url.hostname)
          .sort()
          .join("\n");
280
281
        runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
          for (let blocked of param.Block) {
282
283
284
285
            Services.cookies.removeCookiesWithOriginAttributes(
              "{}",
              blocked.hostname
            );
286
287
288
          }
        });
      }
289

290
291
292
293
294
295
      if (
        param.Default !== undefined ||
        param.AcceptThirdParty !== undefined ||
        param.RejectTracker !== undefined ||
        param.Locked
      ) {
296
297
298
299
        const ACCEPT_COOKIES = 0;
        const REJECT_THIRD_PARTY_COOKIES = 1;
        const REJECT_ALL_COOKIES = 2;
        const REJECT_UNVISITED_THIRD_PARTY = 3;
300
        const REJECT_TRACKER = 4;
301
302
303
304
305

        let newCookieBehavior = ACCEPT_COOKIES;
        if (param.Default !== undefined && !param.Default) {
          newCookieBehavior = REJECT_ALL_COOKIES;
        } else if (param.AcceptThirdParty) {
306
          if (param.AcceptThirdParty == "never") {
307
308
309
310
            newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
          } else if (param.AcceptThirdParty == "from-visited") {
            newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
          }
311
312
        } else if (param.RejectTracker !== undefined && param.RejectTracker) {
          newCookieBehavior = REJECT_TRACKER;
313
314
        }

315
316
317
318
319
        setDefaultPref(
          "network.cookie.cookieBehavior",
          newCookieBehavior,
          param.Locked
        );
320
321
322
323
324
325
326
327
328
329
330
      }

      const KEEP_COOKIES_UNTIL_EXPIRATION = 0;
      const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2;

      if (param.ExpireAtSessionEnd !== undefined || param.Locked) {
        let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION;
        if (param.ExpireAtSessionEnd) {
          newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION;
        }

331
332
333
334
335
        setDefaultPref(
          "network.cookie.lifetimePolicy",
          newLifetimePolicy,
          param.Locked
        );
336
      }
337
    },
338
339
  },

340
  DefaultDownloadDirectory: {
341
342
343
344
345
346
347
    onBeforeAddons(manager, param) {
      setDefaultPref("browser.download.dir", replacePathVariables(param));
      // If a custom download directory is being used, just lock folder list to 2.
      setAndLockPref("browser.download.folderList", 2);
    },
  },

348
  DisableAppUpdate: {
349
350
351
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("appUpdate");
352
      }
353
    },
354
355
  },

356
  DisableBuiltinPDFViewer: {
357
    onBeforeAddons(manager, param) {
358
      if (param) {
359
        setAndLockPref("pdfjs.disabled", true);
360
      }
361
    },
362
363
  },

364
  DisableDeveloperTools: {
365
366
367
368
369
370
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("devtools.policy.disabled", true);
        setAndLockPref("devtools.chrome.enabled", false);

        manager.disallowFeature("devtools");
371
372
373
        blockAboutPage(manager, "about:devtools");
        blockAboutPage(manager, "about:debugging");
        blockAboutPage(manager, "about:devtools-toolbox");
374
      }
375
    },
376
377
  },

378
  DisableFeedbackCommands: {
379
380
381
382
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("feedbackCommands");
      }
383
    },
384
385
  },

386
  DisableFirefoxAccounts: {
387
388
389
390
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("identity.fxaccounts.enabled", false);
      }
391
    },
392
393
  },

394
  DisableFirefoxScreenshots: {
395
    onBeforeAddons(manager, param) {
396
      if (param) {
397
398
        setAndLockPref("extensions.screenshots.disabled", true);
      }
399
    },
400
401
  },

402
  DisableFirefoxStudies: {
403
    onBeforeAddons(manager, param) {
404
      if (param) {
405
        manager.disallowFeature("Shield");
406
407
408
409
410
411
412
413
        setAndLockPref(
          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
          false
        );
        setAndLockPref(
          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
          false
        );
414
      }
415
    },
416
417
  },

418
  DisableForgetButton: {
419
420
    onProfileAfterChange(manager, param) {
      if (param) {
421
        setAndLockPref("privacy.panicButton.enabled", false);
422
      }
423
    },
424
425
  },

426
  DisableFormHistory: {
427
    onBeforeUIStartup(manager, param) {
428
      if (param) {
429
430
        setAndLockPref("browser.formfill.enable", false);
      }
431
    },
432
433
  },

434
  DisableMasterPasswordCreation: {
435
436
437
438
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("createMasterPassword");
      }
439
    },
440
441
  },

442
  DisablePocket: {
443
444
445
446
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.pocket.enabled", false);
      }
447
    },
448
449
  },

450
  DisablePrivateBrowsing: {
451
452
453
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("privatebrowsing");
454
        blockAboutPage(manager, "about:privatebrowsing", true);
455
456
        setAndLockPref("browser.privatebrowsing.autostart", false);
      }
457
    },
458
459
  },

460
  DisableProfileImport: {
461
462
463
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileImport");
464
465
466
467
        setAndLockPref(
          "browser.newtabpage.activity-stream.migrationExpired",
          true
        );
468
      }
469
    },
470
471
  },

472
  DisableProfileRefresh: {
473
474
475
476
477
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileRefresh");
        setAndLockPref("browser.disableResetPrompt", true);
      }
478
    },
479
480
  },

481
  DisableSafeMode: {
482
483
484
485
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("safeMode");
      }
486
    },
487
488
  },

489
  DisableSecurityBypass: {
490
    onBeforeUIStartup(manager, param) {
491
      if ("InvalidCertificate" in param) {
492
493
494
495
        setAndLockPref(
          "security.certerror.hideAddException",
          param.InvalidCertificate
        );
496
497
      }

498
      if ("SafeBrowsing" in param) {
499
500
501
502
        setAndLockPref(
          "browser.safebrowsing.allowOverride",
          !param.SafeBrowsing
        );
503
      }
504
    },
505
506
  },

507
  DisableSetDesktopBackground: {
508
509
    onBeforeUIStartup(manager, param) {
      if (param) {
510
        manager.disallowFeature("setDesktopBackground");
511
      }
512
    },
513
514
  },

515
  DisableSystemAddonUpdate: {
516
517
518
519
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("SysAddonUpdate");
      }
520
    },
521
522
  },

523
  DisableTelemetry: {
524
525
526
527
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
528
        blockAboutPage(manager, "about:telemetry");
529
      }
530
    },
531
532
  },

533
  DisplayBookmarksToolbar: {
534
    onBeforeUIStartup(manager, param) {
535
536
537
538
539
      let value = (!param).toString();
      // This policy is meant to change the default behavior, not to force it.
      // If this policy was alreay applied and the user chose to re-hide the
      // bookmarks toolbar, do not show it again.
      runOncePerModification("displayBookmarksToolbar", value, () => {
540
541
542
543
544
545
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "PersonalToolbar",
          "collapsed",
          value
        );
546
      });
547
    },
548
549
  },

550
  DisplayMenuBar: {
551
    onBeforeUIStartup(manager, param) {
552
      let value = (!param).toString();
553
554
555
      // This policy is meant to change the default behavior, not to force it.
      // If this policy was alreay applied and the user chose to re-hide the
      // menu bar, do not show it again.
556
      runOncePerModification("displayMenuBar", value, () => {
557
558
559
560
561
562
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "toolbar-menubar",
          "autohide",
          value
        );
563
      });
564
    },
565
566
  },

567
  DNSOverHTTPS: {
568
569
570
    onBeforeAddons(manager, param) {
      if ("Enabled" in param) {
        let mode = param.Enabled ? 2 : 5;
571
        setDefaultPref("network.trr.mode", mode, param.Locked);
572
573
      }
      if (param.ProviderURL) {
574
        setDefaultPref("network.trr.uri", param.ProviderURL.href, param.Locked);
575
576
577
578
      }
    },
  },

579
  DontCheckDefaultBrowser: {
580
    onBeforeUIStartup(manager, param) {
581
      setAndLockPref("browser.shell.checkDefaultBrowser", !param);
582
    },
583
584
  },

585
  DownloadDirectory: {
586
587
588
589
    onBeforeAddons(manager, param) {
      setAndLockPref("browser.download.dir", replacePathVariables(param));
      // If a custom download directory is being used, just lock folder list to 2.
      setAndLockPref("browser.download.folderList", 2);
590
591
592
      // Per Chrome spec, user can't choose to download every time
      // if this is set.
      setAndLockPref("browser.download.useDownloadDir", true);
593
594
595
    },
  },

596
  EnableTrackingProtection: {
597
    onBeforeUIStartup(manager, param) {
598
      if (param.Value) {
599
600
601
602
603
604
605
606
607
608
        setDefaultPref(
          "privacy.trackingprotection.enabled",
          true,
          param.Locked
        );
        setDefaultPref(
          "privacy.trackingprotection.pbmode.enabled",
          true,
          param.Locked
        );
609
      } else {
610
611
        setAndLockPref("privacy.trackingprotection.enabled", false);
        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
612
      }
613
    },
614
615
  },

616
  Extensions: {
617
    onBeforeUIStartup(manager, param) {
618
619
      let uninstallingPromise = Promise.resolve();
      if ("Uninstall" in param) {
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
        uninstallingPromise = runOncePerModification(
          "extensionsUninstall",
          JSON.stringify(param.Uninstall),
          async () => {
            // If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't
            // changed, which will allow add-ons to be updated.
            Services.prefs.clearUserPref(
              "browser.policies.runOncePerModification.extensionsInstall"
            );
            let addons = await AddonManager.getAddonsByIDs(param.Uninstall);
            for (let addon of addons) {
              if (addon) {
                try {
                  await addon.uninstall();
                } catch (e) {
                  // This can fail for add-ons that can't be uninstalled.
                  log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
                }
638
639
640
              }
            }
          }
641
        );
642
      }
643
      if ("Install" in param) {
644
645
646
647
648
649
650
        runOncePerModification(
          "extensionsInstall",
          JSON.stringify(param.Install),
          async () => {
            await uninstallingPromise;
            for (let location of param.Install) {
              let uri;
651
              try {
652
653
654
655
656
657
658
659
660
661
662
663
                uri = Services.io.newURI(location);
              } catch (e) {
                // If it's not a URL, it's probably a file path.
                // Assume location is a file path
                // This is done for legacy support (old API)
                try {
                  let xpiFile = new FileUtils.File(location);
                  uri = Services.io.newFileURI(xpiFile);
                } catch (ex) {
                  log.error(`Invalid extension path location - ${location}`);
                  return;
                }
664
              }
665
              installAddonFromURL(uri.spec);
666
            }
667
          }
668
        );
669
670
671
      }
      if ("Locked" in param) {
        for (let ID of param.Locked) {
672
673
          manager.disallowFeature(`uninstall-extension:${ID}`);
          manager.disallowFeature(`disable-extension:${ID}`);
674
675
        }
      }
676
    },
677
678
  },

679
  ExtensionSettings: {
680
    onBeforeAddons(manager, param) {
681
682
683
      try {
        manager.setExtensionSettings(param);
      } catch (e) {
684
        log.error("Invalid ExtensionSettings");
685
686
687
688
689
690
      }
    },
    async onBeforeUIStartup(manager, param) {
      let extensionSettings = param;
      let blockAllExtensions = false;
      if ("*" in extensionSettings) {
691
692
693
694
        if (
          "installation_mode" in extensionSettings["*"] &&
          extensionSettings["*"].installation_mode == "blocked"
        ) {
695
696
697
698
699
700
701
          blockAllExtensions = true;
          // Turn off discovery pane in about:addons
          setAndLockPref("extensions.getAddons.showPane", false);
          // Block about:debugging
          blockAboutPage(manager, "about:debugging");
        }
      }
702
      let { addons } = await AddonManager.getActiveAddons();
703
704
705
706
707
708
709
      let allowedExtensions = [];
      for (let extensionID in extensionSettings) {
        if (extensionID == "*") {
          // Ignore global settings
          continue;
        }
        if ("installation_mode" in extensionSettings[extensionID]) {
710
711
712
713
714
715
          if (
            extensionSettings[extensionID].installation_mode ==
              "force_installed" ||
            extensionSettings[extensionID].installation_mode ==
              "normal_installed"
          ) {
716
717
718
719
            if (!extensionSettings[extensionID].install_url) {
              throw new Error(`Missing install_url for ${extensionID}`);
            }
            if (!addons.find(addon => addon.id == extensionID)) {
720
721
722
723
              installAddonFromURL(
                extensionSettings[extensionID].install_url,
                extensionID
              );
724
725
            }
            manager.disallowFeature(`uninstall-extension:${extensionID}`);
726
727
728
729
            if (
              extensionSettings[extensionID].installation_mode ==
              "force_installed"
            ) {
730
731
732
              manager.disallowFeature(`disable-extension:${extensionID}`);
            }
            allowedExtensions.push(extensionID);
733
734
735
          } else if (
            extensionSettings[extensionID].installation_mode == "allowed"
          ) {
736
            allowedExtensions.push(extensionID);
737
738
739
          } else if (
            extensionSettings[extensionID].installation_mode == "blocked"
          ) {
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
            if (addons.find(addon => addon.id == extensionID)) {
              // Can't use the addon from getActiveAddons since it doesn't have uninstall.
              let addon = await AddonManager.getAddonByID(extensionID);
              try {
                await addon.uninstall();
              } catch (e) {
                // This can fail for add-ons that can't be uninstalled.
                log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
              }
            }
          }
        }
      }
      if (blockAllExtensions) {
        for (let addon of addons) {
          if (addon.isSystem || addon.isBuiltin) {
            continue;
          }
          if (!allowedExtensions.includes(addon.id)) {
            try {
              // Can't use the addon from getActiveAddons since it doesn't have uninstall.
              let addonToUninstall = await AddonManager.getAddonByID(addon.id);
              await addonToUninstall.uninstall();
            } catch (e) {
              // This can fail for add-ons that can't be uninstalled.
              log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
            }
          }
        }
      }
770
771
772
    },
  },

773
  ExtensionUpdate: {
774
775
776
777
778
779
780
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("extensions.update.enabled", param);
      }
    },
  },

781
  FirefoxHome: {
782
783
784
    onBeforeAddons(manager, param) {
      let locked = param.Locked || false;
      if ("Search" in param) {
785
786
787
788
789
        setDefaultPref(
          "browser.newtabpage.activity-stream.showSearch",
          param.Search,
          locked
        );
790
791
      }
      if ("TopSites" in param) {
792
793
794
795
796
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.topsites",
          param.TopSites,
          locked
        );
797
798
      }
      if ("Highlights" in param) {
799
800
801
802
803
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.highlights",
          param.Highlights,
          locked
        );
804
805
      }
      if ("Pocket" in param) {
806
807
808
809
810
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.topstories",
          param.Pocket,
          locked
        );
811
812
      }
      if ("Snippets" in param) {
813
814
815
816
817
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.snippets",
          param.Snippets,
          locked
        );
818
819
820
821
      }
    },
  },

822
  FlashPlugin: {
823
    onBeforeUIStartup(manager, param) {
824
      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
825
826
827
828
829

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;

      let flashPrefVal;
830
      if (param.Default === undefined || param.Default) {
831
832
833
834
835
836
837
838
839
        flashPrefVal = FLASH_ASK_TO_ACTIVATE;
      } else {
        flashPrefVal = FLASH_NEVER_ACTIVATE;
      }
      if (param.Locked) {
        setAndLockPref("plugin.state.flash", flashPrefVal);
      } else if (param.Default !== undefined) {
        setDefaultPref("plugin.state.flash", flashPrefVal);
      }
840
    },
841
842
  },

843
  HardwareAcceleration: {
844
845
846
847
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("layers.acceleration.disabled", true);
      }
848
    },
849
850
  },

851
  Homepage: {
852
853
854
855
    onBeforeUIStartup(manager, param) {
      // |homepages| will be a string containing a pipe-separated ('|') list of
      // URLs because that is what the "Home page" section of about:preferences
      // (and therefore what the pref |browser.startup.homepage|) accepts.
856
857
858
859
860
      if (param.URL) {
        let homepages = param.URL.href;
        if (param.Additional && param.Additional.length > 0) {
          homepages += "|" + param.Additional.map(url => url.href).join("|");
        }
861
        setDefaultPref("browser.startup.homepage", homepages, param.Locked);
862
        if (param.Locked) {
863
864
865
866
867
868
869
870
871
872
873
874
          setAndLockPref(
            "pref.browser.homepage.disable_button.current_page",
            true
          );
          setAndLockPref(
            "pref.browser.homepage.disable_button.bookmark_page",
            true
          );
          setAndLockPref(
            "pref.browser.homepage.disable_button.restore_default",
            true
          );
875
876
877
878
879
        } else {
          runOncePerModification("setHomepage", homepages, () => {
            Services.prefs.clearUserPref("browser.startup.homepage");
          });
        }
880
      }
881
882
883
884
885
886
887
888
889
890
891
892
893
      if (param.StartPage) {
        let prefValue;
        switch (param.StartPage) {
          case "none":
            prefValue = 0;
            break;
          case "homepage":
            prefValue = 1;
            break;
          case "previous-session":
            prefValue = 3;
            break;
        }
894
        setDefaultPref("browser.startup.page", prefValue, param.Locked);
895
      }
896
    },
897
898
  },

899
  InstallAddonsPermission: {
900
    onBeforeUIStartup(manager, param) {
901
902
903
904
905
      if ("Allow" in param) {
        addAllowDenyPermissions("install", param.Allow, null);
      }
      if ("Default" in param) {
        setAndLockPref("xpinstall.enabled", param.Default);
906
        if (!param.Default) {
907
          blockAboutPage(manager, "about:debugging");
908
909
910
911
912
913
914
915
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
            false
          );
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
            false
          );
916
          manager.disallowFeature("xpinstall");
917
        }
918
      }
919
    },
920
921
  },

922
  LocalFileLinks: {
923
924
    onBeforeAddons(manager, param) {
      // If there are existing capabilities, lock them with the policy pref.
925
926
927
      let policyNames = Services.prefs
        .getCharPref("capability.policy.policynames", "")
        .split(" ");
928
929
      policyNames.push("localfilelinks_policy");
      setAndLockPref("capability.policy.policynames", policyNames.join(" "));
930
931
932
933
934
935
936
937
      setAndLockPref(
        "capability.policy.localfilelinks_policy.checkloaduri.enabled",
        "allAccess"
      );
      setAndLockPref(
        "capability.policy.localfilelinks_policy.sites",
        param.join(" ")
      );
938
939
940
    },
  },

941
  NetworkPrediction: {
942
943
944
945
946
947
    onBeforeAddons(manager, param) {
      setAndLockPref("network.dns.disablePrefetch", !param);
      setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
    },
  },

948
  NewTabPage: {
949
950
951
952
953
    onBeforeAddons(manager, param) {
      setAndLockPref("browser.newtabpage.enabled", param);
    },
  },

954
  NoDefaultBookmarks: {
955
956
957
958
    onProfileAfterChange(manager, param) {
      if (param) {
        manager.disallowFeature("defaultBookmarks");
      }
959
    },
960
961
  },

962
  OfferToSaveLogins: {
963
964
    onBeforeUIStartup(manager, param) {
      setAndLockPref("signon.rememberSignons", param);
965
    },
966
967
  },

968
  OverrideFirstRunPage: {
969
    onProfileAfterChange(manager, param) {
970
      let url = param ? param.href : "";
971
      setAndLockPref("startup.homepage_welcome_url", url);
972
    },
973
974
  },

975
  OverridePostUpdatePage: {
976
    onProfileAfterChange(manager, param) {
977
      let url = param ? param.href : "";
978
979
980
981
982
      setAndLockPref("startup.homepage_override_url", url);
      // The pref startup.homepage_override_url is only used
      // as a fallback when the update.xml file hasn't provided
      // a specific post-update URL.
      manager.disallowFeature("postUpdateCustomPage");
983
    },
984
985
  },

986
  Permissions: {
987
988
    onBeforeUIStartup(manager, param) {
      if (param.Camera) {
989
990
991
992
993
        addAllowDenyPermissions(
          "camera",
          param.Camera.Allow,
          param.Camera.Block
        );
994
995
996
997
        setDefaultPermission("camera", param.Camera);
      }

      if (param.Microphone) {
998
999
1000
        addAllowDenyPermissions(
          "microphone",
          param.Microphone.Allow,