Policies.jsm 52.7 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
    onBeforeAddons(manager, param) {
88
89
90
91
92
      let locked = true;
      if ("Locked" in param) {
        locked = param.Locked;
      }

93
      if ("SPNEGO" in param) {
94
        setDefaultPref(
95
          "network.negotiate-auth.trusted-uris",
96
97
          param.SPNEGO.join(", "),
          locked
98
        );
99
100
      }
      if ("Delegated" in param) {
101
        setDefaultPref(
102
          "network.negotiate-auth.delegation-uris",
103
104
          param.Delegated.join(", "),
          locked
105
        );
106
107
      }
      if ("NTLM" in param) {
108
        setDefaultPref(
109
          "network.automatic-ntlm-auth.trusted-uris",
110
111
          param.NTLM.join(", "),
          locked
112
        );
113
      }
114
      if ("AllowNonFQDN" in param) {
115
        if ("NTLM" in param.AllowNonFQDN) {
116
          setDefaultPref(
117
            "network.automatic-ntlm-auth.allow-non-fqdn",
118
119
            param.AllowNonFQDN.NTLM,
            locked
120
          );
121
        }
122
        if ("SPNEGO" in param.AllowNonFQDN) {
123
          setDefaultPref(
124
            "network.negotiate-auth.allow-non-fqdn",
125
126
            param.AllowNonFQDN.SPNEGO,
            locked
127
          );
128
129
        }
      }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
      if ("AllowProxies" in param) {
        if ("NTLM" in param.AllowProxies) {
          setDefaultPref(
            "network.automatic-ntlm-auth.allow-proxies",
            param.AllowProxies.NTLM,
            locked
          );
        }
        if ("SPNEGO" in param.AllowProxies) {
          setDefaultPref(
            "network.negotiate-auth.allow-proxies",
            param.AllowProxies.SPNEGO,
            locked
          );
        }
      }
146
    },
147
148
  },

149
  BlockAboutAddons: {
150
151
    onBeforeUIStartup(manager, param) {
      if (param) {
152
        blockAboutPage(manager, "about:addons", true);
153
      }
154
    },
155
156
  },

157
  BlockAboutConfig: {
158
    onBeforeUIStartup(manager, param) {
159
      if (param) {
160
        blockAboutPage(manager, "about:config");
161
        setAndLockPref("devtools.chrome.enabled", false);
162
      }
163
    },
164
  },
165

166
  BlockAboutProfiles: {
167
168
    onBeforeUIStartup(manager, param) {
      if (param) {
169
        blockAboutPage(manager, "about:profiles");
170
      }
171
    },
172
173
  },

174
  BlockAboutSupport: {
175
176
    onBeforeUIStartup(manager, param) {
      if (param) {
177
        blockAboutPage(manager, "about:support");
178
      }
179
    },
180
181
  },

182
  Bookmarks: {
183
184
    onAllWindowsRestored(manager, param) {
      BookmarksPolicies.processBookmarks(param);
185
    },
186
187
  },

188
  CaptivePortal: {
189
190
191
192
193
    onBeforeAddons(manager, param) {
      setAndLockPref("network.captive-portal-service.enabled", param);
    },
  },

194
  Certificates: {
195
196
    onBeforeAddons(manager, param) {
      if ("ImportEnterpriseRoots" in param) {
197
198
199
200
        setAndLockPref(
          "security.enterprise_roots.enabled",
          param.ImportEnterpriseRoots
        );
201
      }
202
203
204
205
206
207
      if ("Install" in param) {
        (async () => {
          let dirs = [];
          let platform = AppConstants.platform;
          if (platform == "win") {
            dirs = [
208
              // Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
209
              Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
210
211
              // Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
              Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
212
213
214
215
216
217
218
219
            ];
          } 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),
            ];
          }
220
221
222
223
          dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile));
          for (let certfilename of param.Install) {
            let certfile;
            try {
224
225
226
              certfile = Cc["@mozilla.org/file/local;1"].createInstance(
                Ci.nsIFile
              );
227
228
229
230
              certfile.initWithPath(certfilename);
            } catch (e) {
              for (let dir of dirs) {
                certfile = dir.clone();
231
232
233
                certfile.append(
                  platform == "linux" ? "certificates" : "Certificates"
                );
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
                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;
              }
253
254
              let certFile = reader.result;
              let cert;
255
              try {
256
                cert = gCertDB.constructX509(certFile);
257
              } catch (e) {
258
259
260
261
262
263
264
265
266
                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) {
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
                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,"
                      );
                    }
287
                  }
288
                );
289
              }
290
291
            };
            reader.readAsBinaryString(file);
292
293
294
          }
        })();
      }
295
    },
296
297
  },

298
  Cookies: {
299
    onBeforeUIStartup(manager, param) {
300
      addAllowDenyPermissions("cookie", param.Allow, param.Block);
301
302

      if (param.Block) {
303
304
305
        const hosts = param.Block.map(url => url.hostname)
          .sort()
          .join("\n");
306
307
        runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
          for (let blocked of param.Block) {
308
309
310
311
            Services.cookies.removeCookiesWithOriginAttributes(
              "{}",
              blocked.hostname
            );
312
313
314
          }
        });
      }
315

316
317
318
319
320
321
      if (
        param.Default !== undefined ||
        param.AcceptThirdParty !== undefined ||
        param.RejectTracker !== undefined ||
        param.Locked
      ) {
322
323
324
325
        const ACCEPT_COOKIES = 0;
        const REJECT_THIRD_PARTY_COOKIES = 1;
        const REJECT_ALL_COOKIES = 2;
        const REJECT_UNVISITED_THIRD_PARTY = 3;
326
        const REJECT_TRACKER = 4;
327
328
329
330
331

        let newCookieBehavior = ACCEPT_COOKIES;
        if (param.Default !== undefined && !param.Default) {
          newCookieBehavior = REJECT_ALL_COOKIES;
        } else if (param.AcceptThirdParty) {
332
          if (param.AcceptThirdParty == "never") {
333
334
335
336
            newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
          } else if (param.AcceptThirdParty == "from-visited") {
            newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
          }
337
338
        } else if (param.RejectTracker !== undefined && param.RejectTracker) {
          newCookieBehavior = REJECT_TRACKER;
339
340
        }

341
342
343
344
345
        setDefaultPref(
          "network.cookie.cookieBehavior",
          newCookieBehavior,
          param.Locked
        );
346
347
348
349
350
351
352
353
354
355
356
      }

      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;
        }

357
358
359
360
361
        setDefaultPref(
          "network.cookie.lifetimePolicy",
          newLifetimePolicy,
          param.Locked
        );
362
      }
363
    },
364
365
  },

366
  DefaultDownloadDirectory: {
367
368
369
370
371
372
373
    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);
    },
  },

374
  DisableAppUpdate: {
375
376
377
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("appUpdate");
378
      }
379
    },
380
381
  },

382
  DisableBuiltinPDFViewer: {
383
    onBeforeAddons(manager, param) {
384
      if (param) {
385
        setAndLockPref("pdfjs.disabled", true);
386
      }
387
    },
388
389
  },

390
  DisableDeveloperTools: {
391
392
393
394
395
396
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("devtools.policy.disabled", true);
        setAndLockPref("devtools.chrome.enabled", false);

        manager.disallowFeature("devtools");
397
398
399
        blockAboutPage(manager, "about:devtools");
        blockAboutPage(manager, "about:debugging");
        blockAboutPage(manager, "about:devtools-toolbox");
400
      }
401
    },
402
403
  },

404
  DisableFeedbackCommands: {
405
406
407
408
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("feedbackCommands");
      }
409
    },
410
411
  },

412
  DisableFirefoxAccounts: {
413
414
415
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("identity.fxaccounts.enabled", false);
416
        setAndLockPref("trailhead.firstrun.branches", "nofirstrun");
417
      }
418
    },
419
420
  },

421
  DisableFirefoxScreenshots: {
422
    onBeforeAddons(manager, param) {
423
      if (param) {
424
425
        setAndLockPref("extensions.screenshots.disabled", true);
      }
426
    },
427
428
  },

429
  DisableFirefoxStudies: {
430
    onBeforeAddons(manager, param) {
431
      if (param) {
432
        manager.disallowFeature("Shield");
433
434
435
436
437
438
439
440
        setAndLockPref(
          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
          false
        );
        setAndLockPref(
          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
          false
        );
441
      }
442
    },
443
444
  },

445
  DisableForgetButton: {
446
447
    onProfileAfterChange(manager, param) {
      if (param) {
448
        setAndLockPref("privacy.panicButton.enabled", false);
449
      }
450
    },
451
452
  },

453
  DisableFormHistory: {
454
    onBeforeUIStartup(manager, param) {
455
      if (param) {
456
457
        setAndLockPref("browser.formfill.enable", false);
      }
458
    },
459
460
  },

461
  DisableMasterPasswordCreation: {
462
463
464
465
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("createMasterPassword");
      }
466
    },
467
468
  },

469
470
471
472
473
474
475
476
  DisablePasswordReveal: {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("passwordReveal");
      }
    },
  },

477
  DisablePocket: {
478
479
480
481
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.pocket.enabled", false);
      }
482
    },
483
484
  },

485
  DisablePrivateBrowsing: {
486
487
488
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("privatebrowsing");
489
        blockAboutPage(manager, "about:privatebrowsing", true);
490
491
        setAndLockPref("browser.privatebrowsing.autostart", false);
      }
492
    },
493
494
  },

495
  DisableProfileImport: {
496
497
498
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileImport");
499
500
501
502
        setAndLockPref(
          "browser.newtabpage.activity-stream.migrationExpired",
          true
        );
503
      }
504
    },
505
506
  },

507
  DisableProfileRefresh: {
508
509
510
511
512
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileRefresh");
        setAndLockPref("browser.disableResetPrompt", true);
      }
513
    },
514
515
  },

516
  DisableSafeMode: {
517
518
519
520
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("safeMode");
      }
521
    },
522
523
  },

524
  DisableSecurityBypass: {
525
    onBeforeUIStartup(manager, param) {
526
      if ("InvalidCertificate" in param) {
527
528
529
530
        setAndLockPref(
          "security.certerror.hideAddException",
          param.InvalidCertificate
        );
531
532
      }

533
      if ("SafeBrowsing" in param) {
534
535
536
537
        setAndLockPref(
          "browser.safebrowsing.allowOverride",
          !param.SafeBrowsing
        );
538
      }
539
    },
540
541
  },

542
  DisableSetDesktopBackground: {
543
544
    onBeforeUIStartup(manager, param) {
      if (param) {
545
        manager.disallowFeature("setDesktopBackground");
546
      }
547
    },
548
549
  },

550
  DisableSystemAddonUpdate: {
551
552
553
554
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("SysAddonUpdate");
      }
555
    },
556
557
  },

558
  DisableTelemetry: {
559
560
561
562
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
563
        blockAboutPage(manager, "about:telemetry");
564
      }
565
    },
566
567
  },

568
  DisplayBookmarksToolbar: {
569
    onBeforeUIStartup(manager, param) {
570
571
572
573
574
      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, () => {
575
576
577
578
579
580
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "PersonalToolbar",
          "collapsed",
          value
        );
581
      });
582
    },
583
584
  },

585
  DisplayMenuBar: {
586
    onBeforeUIStartup(manager, param) {
587
588
589
590
591
      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
      // menu bar, do not show it again.
      runOncePerModification("displayMenuBar", value, () => {
592
593
594
595
596
597
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "toolbar-menubar",
          "autohide",
          value
        );
598
      });
599
    },
600
601
  },

602
  DNSOverHTTPS: {
603
604
605
    onBeforeAddons(manager, param) {
      if ("Enabled" in param) {
        let mode = param.Enabled ? 2 : 5;
606
        setDefaultPref("network.trr.mode", mode, param.Locked);
607
608
      }
      if (param.ProviderURL) {
609
        setDefaultPref("network.trr.uri", param.ProviderURL.href, param.Locked);
610
611
612
613
      }
    },
  },

614
  DontCheckDefaultBrowser: {
615
    onBeforeUIStartup(manager, param) {
616
      setAndLockPref("browser.shell.checkDefaultBrowser", !param);
617
    },
618
619
  },

620
  DownloadDirectory: {
621
622
623
624
    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);
625
626
627
      // Per Chrome spec, user can't choose to download every time
      // if this is set.
      setAndLockPref("browser.download.useDownloadDir", true);
628
629
630
    },
  },

631
  EnableTrackingProtection: {
632
    onBeforeUIStartup(manager, param) {
633
      if (param.Value) {
634
635
636
637
638
639
640
641
642
643
        setDefaultPref(
          "privacy.trackingprotection.enabled",
          true,
          param.Locked
        );
        setDefaultPref(
          "privacy.trackingprotection.pbmode.enabled",
          true,
          param.Locked
        );
644
      } else {
645
646
        setAndLockPref("privacy.trackingprotection.enabled", false);
        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
647
      }
648
649
650
651
652
653
654
655
656
657
658
659
660
661
      if ("Cryptomining" in param) {
        setDefaultPref(
          "privacy.trackingprotection.cryptomining.enabled",
          param.Cryptomining,
          param.Locked
        );
      }
      if ("Fingerprinting" in param) {
        setDefaultPref(
          "privacy.trackingprotection.fingerprinting.enabled",
          param.Fingerprinting,
          param.Locked
        );
      }
662
    },
663
664
  },

665
  Extensions: {
666
    onBeforeUIStartup(manager, param) {
667
668
      let uninstallingPromise = Promise.resolve();
      if ("Uninstall" in param) {
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
        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.`);
                }
687
688
689
              }
            }
          }
690
        );
691
      }
692
      if ("Install" in param) {
693
694
695
696
697
698
699
        runOncePerModification(
          "extensionsInstall",
          JSON.stringify(param.Install),
          async () => {
            await uninstallingPromise;
            for (let location of param.Install) {
              let uri;
700
              try {
701
702
                // We need to try as a file first because
                // Windows paths are valid URIs.
703
                // This is done for legacy support (old API)
704
705
706
707
                let xpiFile = new FileUtils.File(location);
                uri = Services.io.newFileURI(xpiFile);
              } catch (e) {
                uri = Services.io.newURI(location);
708
              }
709
              installAddonFromURL(uri.spec);
710
            }
711
          }
712
        );
713
714
715
      }
      if ("Locked" in param) {
        for (let ID of param.Locked) {
716
717
          manager.disallowFeature(`uninstall-extension:${ID}`);
          manager.disallowFeature(`disable-extension:${ID}`);
718
719
        }
      }
720
    },
721
722
  },

723
  ExtensionSettings: {
724
    onBeforeAddons(manager, param) {
725
726
727
      try {
        manager.setExtensionSettings(param);
      } catch (e) {
728
        log.error("Invalid ExtensionSettings");
729
730
731
732
733
734
      }
    },
    async onBeforeUIStartup(manager, param) {
      let extensionSettings = param;
      let blockAllExtensions = false;
      if ("*" in extensionSettings) {
735
736
737
738
        if (
          "installation_mode" in extensionSettings["*"] &&
          extensionSettings["*"].installation_mode == "blocked"
        ) {
739
740
741
742
743
744
745
          blockAllExtensions = true;
          // Turn off discovery pane in about:addons
          setAndLockPref("extensions.getAddons.showPane", false);
          // Block about:debugging
          blockAboutPage(manager, "about:debugging");
        }
      }
746
      let { addons } = await AddonManager.getActiveAddons();
747
748
749
750
751
752
753
      let allowedExtensions = [];
      for (let extensionID in extensionSettings) {
        if (extensionID == "*") {
          // Ignore global settings
          continue;
        }
        if ("installation_mode" in extensionSettings[extensionID]) {
754
755
756
757
758
759
          if (
            extensionSettings[extensionID].installation_mode ==
              "force_installed" ||
            extensionSettings[extensionID].installation_mode ==
              "normal_installed"
          ) {
760
761
762
763
            if (!extensionSettings[extensionID].install_url) {
              throw new Error(`Missing install_url for ${extensionID}`);
            }
            if (!addons.find(addon => addon.id == extensionID)) {
764
765
766
767
              installAddonFromURL(
                extensionSettings[extensionID].install_url,
                extensionID
              );
768
769
            }
            manager.disallowFeature(`uninstall-extension:${extensionID}`);
770
771
772
773
            if (
              extensionSettings[extensionID].installation_mode ==
              "force_installed"
            ) {
774
775
776
              manager.disallowFeature(`disable-extension:${extensionID}`);
            }
            allowedExtensions.push(extensionID);
777
778
779
          } else if (
            extensionSettings[extensionID].installation_mode == "allowed"
          ) {
780
            allowedExtensions.push(extensionID);
781
782
783
          } else if (
            extensionSettings[extensionID].installation_mode == "blocked"
          ) {
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
            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) {
799
800
801
802
803
          if (
            addon.isSystem ||
            addon.isBuiltin ||
            !(addon.scope & AddonManager.SCOPE_PROFILE)
          ) {
804
805
806
807
808
809
810
811
812
813
814
815
816
817
            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.`);
            }
          }
        }
      }
818
819
820
    },
  },

821
  ExtensionUpdate: {
822
823
824
825
826
827
828
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("extensions.update.enabled", param);
      }
    },
  },

829
  FirefoxHome: {
830
831
832
    onBeforeAddons(manager, param) {
      let locked = param.Locked || false;
      if ("Search" in param) {
833
834
835
836
837
        setDefaultPref(
          "browser.newtabpage.activity-stream.showSearch",
          param.Search,
          locked
        );
838
839
      }
      if ("TopSites" in param) {
840
841
842
843
844
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.topsites",
          param.TopSites,
          locked
        );
845
846
      }
      if ("Highlights" in param) {
847
848
849
850
851
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.highlights",
          param.Highlights,
          locked
        );
852
853
      }
      if ("Pocket" in param) {
854
855
856
857
858
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.topstories",
          param.Pocket,
          locked
        );
859
860
      }
      if ("Snippets" in param) {
861
862
863
864
865
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.snippets",
          param.Snippets,
          locked
        );
866
867
868
869
      }
    },
  },

870
  FlashPlugin: {
871
    onBeforeUIStartup(manager, param) {
872
      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
873
874
875
876
877

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;

      let flashPrefVal;
878
      if (param.Default === undefined || param.Default) {
879
880
881
882
883
884
885
886
887
        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);
      }
888
    },
889
890
  },

891
  HardwareAcceleration: {
892
893
894
895
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("layers.acceleration.disabled", true);
      }
896
    },
897
898
  },

899
  Homepage: {
900
901
902
903
    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.
904
905
      if (param.URL) {
        let homepages = param.URL.href;
906
        if (param.Additional && param.Additional.length) {
907
908
          homepages += "|" + param.Additional.map(url => url.href).join("|");
        }
909
        setDefaultPref("browser.startup.homepage", homepages, param.Locked);
910
        if (param.Locked) {
911
912
913
914
915
916
917
918
919
920
921
922
          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
          );
923
        } else {
924
925
          // Clear out old run once modification that is no longer used.
          clearRunOnceModification("setHomepage");
926
        }
927
      }
928
929
930
931
932
933
934
935
936
937
938
939
940
      if (param.StartPage) {
        let prefValue;
        switch (param.StartPage) {
          case "none":
            prefValue = 0;
            break;
          case "homepage":
            prefValue = 1;
            break;
          case "previous-session":
            prefValue = 3;
            break;
        }
941
        setDefaultPref("browser.startup.page", prefValue, param.Locked);
942
      }
943
    },
944
945
  },

946
  InstallAddonsPermission: {
947
    onBeforeUIStartup(manager, param) {
948
949
950
951
952
      if ("Allow" in param) {
        addAllowDenyPermissions("install", param.Allow, null);
      }
      if ("Default" in param) {
        setAndLockPref("xpinstall.enabled", param.Default);
953
        if (!param.Default) {
954
          blockAboutPage(manager, "about:debugging");
955
956
957
958
959
960
961
962
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
            false
          );
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
            false
          );
963
          manager.disallowFeature("xpinstall");
964
        }
965
      }
966
    },
967
968
  },

969
970
971
972
  LegacyProfiles: {
    // Handled in nsToolkitProfileService.cpp (Windows only)
  },

973
  LocalFileLinks: {
974
975
    onBeforeAddons(manager, param) {
      // If there are existing capabilities, lock them with the policy pref.
976
977
978
      let policyNames = Services.prefs
        .getCharPref("capability.policy.policynames", "")
        .split(" ");
979
980
      policyNames.push("localfilelinks_policy");
      setAndLockPref("capability.policy.policynames", policyNames.join(" "));
981
982
983
984
985
986
987
988
      setAndLockPref(
        "capability.policy.localfilelinks_policy.checkloaduri.enabled",
        "allAccess"
      );
      setAndLockPref(
        "capability.policy.localfilelinks_policy.sites",
        param.join(" ")
      );
989
990
991
    },
  },

992
  NetworkPrediction: {
993
994
995
996
997
998
    onBeforeAddons(manager, param) {
      setAndLockPref("network.dns.disablePrefetch", !param);
      setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
    },
  },

999
  NewTabPage: {
1000
1001
1002
1003
1004
    onBeforeAddons(manager, param) {
      setAndLockPref("browser.newtabpage.enabled", param);
    },
  },

1005
  NoDefaultBookmarks: {
1006
1007
1008
1009
    onProfileAfterChange(manager, param) {
      if (param) {
        manager.disallowFeature("defaultBookmarks");
      }
1010
    },
1011
1012
  },

1013
  OfferToSaveLogins: {
1014
1015
    onBeforeUIStartup(manager, param) {
      setAndLockPref("signon.rememberSignons", param);
1016
    },
1017
1018
  },

1019
1020
1021
1022
1023
1024
  OfferToSaveLoginsDefault: {
    onBeforeUIStartup(manager, param) {
      setDefaultPref("signon.rememberSignons", param);
    },