Policies.jsm 53.8 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
              let certFile = reader.result;
254
255
256
257
              let certFileArray = [];
              for (let i = 0; i < certFile.length; i++) {
                certFileArray.push(certFile.charCodeAt(i));
              }
258
              let cert;
259
              try {
260
                cert = gCertDB.constructX509(certFileArray);
261
              } catch (e) {
262
263
264
265
266
267
268
269
270
                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) {
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
                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,"
                      );
                    }
291
                  }
292
                );
293
              }
294
295
            };
            reader.readAsBinaryString(file);
296
297
298
          }
        })();
      }
299
    },
300
301
  },

302
  Cookies: {
303
    onBeforeUIStartup(manager, param) {
304
      addAllowDenyPermissions("cookie", param.Allow, param.Block);
305
306

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

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

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

345
346
347
348
349
        setDefaultPref(
          "network.cookie.cookieBehavior",
          newCookieBehavior,
          param.Locked
        );
350
351
352
353
354
355
356
357
358
359
360
      }

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

361
362
363
364
365
        setDefaultPref(
          "network.cookie.lifetimePolicy",
          newLifetimePolicy,
          param.Locked
        );
366
      }
367
    },
368
369
  },

370
  DefaultDownloadDirectory: {
371
372
373
374
375
376
377
    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);
    },
  },

378
  DisableAppUpdate: {
379
380
381
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("appUpdate");
382
      }
383
    },
384
385
  },

386
  DisableBuiltinPDFViewer: {
387
    onBeforeAddons(manager, param) {
388
      if (param) {
389
        setAndLockPref("pdfjs.disabled", true);
390
      }
391
    },
392
393
  },

394
  DisableDeveloperTools: {
395
396
397
398
399
400
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("devtools.policy.disabled", true);
        setAndLockPref("devtools.chrome.enabled", false);

        manager.disallowFeature("devtools");
401
402
403
        blockAboutPage(manager, "about:devtools");
        blockAboutPage(manager, "about:debugging");
        blockAboutPage(manager, "about:devtools-toolbox");
404
      }
405
    },
406
407
  },

408
  DisableFeedbackCommands: {
409
410
411
412
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("feedbackCommands");
      }
413
    },
414
415
  },

416
  DisableFirefoxAccounts: {
417
418
419
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("identity.fxaccounts.enabled", false);
420
        setAndLockPref("trailhead.firstrun.branches", "nofirstrun");
421
      }
422
    },
423
424
  },

425
  DisableFirefoxScreenshots: {
426
    onBeforeAddons(manager, param) {
427
      if (param) {
428
429
        setAndLockPref("extensions.screenshots.disabled", true);
      }
430
    },
431
432
  },

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

449
  DisableForgetButton: {
450
451
    onProfileAfterChange(manager, param) {
      if (param) {
452
        setAndLockPref("privacy.panicButton.enabled", false);
453
      }
454
    },
455
456
  },

457
  DisableFormHistory: {
458
    onBeforeUIStartup(manager, param) {
459
      if (param) {
460
461
        setAndLockPref("browser.formfill.enable", false);
      }
462
    },
463
464
  },

465
  DisableMasterPasswordCreation: {
466
467
468
469
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("createMasterPassword");
      }
470
    },
471
472
  },

473
474
475
476
477
478
479
480
  DisablePasswordReveal: {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("passwordReveal");
      }
    },
  },

481
  DisablePocket: {
482
483
484
485
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.pocket.enabled", false);
      }
486
    },
487
488
  },

489
  DisablePrivateBrowsing: {
490
491
492
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("privatebrowsing");
493
        blockAboutPage(manager, "about:privatebrowsing", true);
494
495
        setAndLockPref("browser.privatebrowsing.autostart", false);
      }
496
    },
497
498
  },

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

511
  DisableProfileRefresh: {
512
513
514
515
516
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileRefresh");
        setAndLockPref("browser.disableResetPrompt", true);
      }
517
    },
518
519
  },

520
  DisableSafeMode: {
521
522
523
524
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("safeMode");
      }
525
    },
526
527
  },

528
  DisableSecurityBypass: {
529
    onBeforeUIStartup(manager, param) {
530
      if ("InvalidCertificate" in param) {
531
532
533
534
        setAndLockPref(
          "security.certerror.hideAddException",
          param.InvalidCertificate
        );
535
536
      }

537
      if ("SafeBrowsing" in param) {
538
539
540
541
        setAndLockPref(
          "browser.safebrowsing.allowOverride",
          !param.SafeBrowsing
        );
542
      }
543
    },
544
545
  },

546
  DisableSetDesktopBackground: {
547
548
    onBeforeUIStartup(manager, param) {
      if (param) {
549
        manager.disallowFeature("setDesktopBackground");
550
      }
551
    },
552
553
  },

554
  DisableSystemAddonUpdate: {
555
556
557
558
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("SysAddonUpdate");
      }
559
    },
560
561
  },

562
  DisableTelemetry: {
563
564
565
566
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
567
        blockAboutPage(manager, "about:telemetry");
568
      }
569
    },
570
571
  },

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

589
  DisplayMenuBar: {
590
    onBeforeUIStartup(manager, param) {
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
      let value;
      if (
        typeof param === "boolean" ||
        param == "default-on" ||
        param == "default-off"
      ) {
        switch (param) {
          case "default-on":
            value = "false";
            break;
          case "default-off":
            value = "true";
            break;
          default:
            value = (!param).toString();
            break;
        }
        // This policy is meant to change the default behavior, not to force it.
        // If this policy was already applied and the user chose to re-hide the
        // menu bar, do not show it again.
        runOncePerModification("displayMenuBar", value, () => {
          gXulStore.setValue(
            BROWSER_DOCUMENT_URL,
            "toolbar-menubar",
            "autohide",
            value
          );
        });
      } else {
        switch (param) {
          case "always":
            value = "false";
            break;
          case "never":
            // Make sure Alt key doesn't show the menubar
            setAndLockPref("ui.key.menuAccessKeyFocuses", false);
            value = "true";
            break;
        }
630
631
632
633
634
635
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "toolbar-menubar",
          "autohide",
          value
        );
636
637
        manager.disallowFeature("hideShowMenuBar");
      }
638
    },
639
640
  },

641
  DNSOverHTTPS: {
642
643
644
    onBeforeAddons(manager, param) {
      if ("Enabled" in param) {
        let mode = param.Enabled ? 2 : 5;
645
        setDefaultPref("network.trr.mode", mode, param.Locked);
646
647
      }
      if (param.ProviderURL) {
648
        setDefaultPref("network.trr.uri", param.ProviderURL.href, param.Locked);
649
650
651
652
      }
    },
  },

653
  DontCheckDefaultBrowser: {
654
    onBeforeUIStartup(manager, param) {
655
      setAndLockPref("browser.shell.checkDefaultBrowser", !param);
656
    },
657
658
  },

659
  DownloadDirectory: {
660
661
662
663
    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);
664
665
666
      // Per Chrome spec, user can't choose to download every time
      // if this is set.
      setAndLockPref("browser.download.useDownloadDir", true);
667
668
669
    },
  },

670
  EnableTrackingProtection: {
671
    onBeforeUIStartup(manager, param) {
672
      if (param.Value) {
673
674
675
676
677
678
679
680
681
682
        setDefaultPref(
          "privacy.trackingprotection.enabled",
          true,
          param.Locked
        );
        setDefaultPref(
          "privacy.trackingprotection.pbmode.enabled",
          true,
          param.Locked
        );
683
      } else {
684
685
        setAndLockPref("privacy.trackingprotection.enabled", false);
        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
686
      }
687
688
689
690
691
692
693
694
695
696
697
698
699
700
      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
        );
      }
701
    },
702
703
  },

704
  Extensions: {
705
    onBeforeUIStartup(manager, param) {
706
707
      let uninstallingPromise = Promise.resolve();
      if ("Uninstall" in param) {
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
        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.`);
                }
726
727
728
              }
            }
          }
729
        );
730
      }
731
      if ("Install" in param) {
732
733
734
735
736
737
738
        runOncePerModification(
          "extensionsInstall",
          JSON.stringify(param.Install),
          async () => {
            await uninstallingPromise;
            for (let location of param.Install) {
              let uri;
739
              try {
740
741
                // We need to try as a file first because
                // Windows paths are valid URIs.
742
                // This is done for legacy support (old API)
743
744
745
746
                let xpiFile = new FileUtils.File(location);
                uri = Services.io.newFileURI(xpiFile);
              } catch (e) {
                uri = Services.io.newURI(location);
747
              }
748
              installAddonFromURL(uri.spec);
749
            }
750
          }
751
        );
752
753
754
      }
      if ("Locked" in param) {
        for (let ID of param.Locked) {
755
756
          manager.disallowFeature(`uninstall-extension:${ID}`);
          manager.disallowFeature(`disable-extension:${ID}`);
757
758
        }
      }
759
    },
760
761
  },

762
  ExtensionSettings: {
763
    onBeforeAddons(manager, param) {
764
765
766
      try {
        manager.setExtensionSettings(param);
      } catch (e) {
767
        log.error("Invalid ExtensionSettings");
768
769
770
771
772
773
      }
    },
    async onBeforeUIStartup(manager, param) {
      let extensionSettings = param;
      let blockAllExtensions = false;
      if ("*" in extensionSettings) {
774
775
776
777
        if (
          "installation_mode" in extensionSettings["*"] &&
          extensionSettings["*"].installation_mode == "blocked"
        ) {
778
779
780
781
782
783
784
          blockAllExtensions = true;
          // Turn off discovery pane in about:addons
          setAndLockPref("extensions.getAddons.showPane", false);
          // Block about:debugging
          blockAboutPage(manager, "about:debugging");
        }
      }
785
      let addons = await AddonManager.getAllAddons();
786
787
788
789
790
791
792
      let allowedExtensions = [];
      for (let extensionID in extensionSettings) {
        if (extensionID == "*") {
          // Ignore global settings
          continue;
        }
        if ("installation_mode" in extensionSettings[extensionID]) {
793
794
795
796
797
798
          if (
            extensionSettings[extensionID].installation_mode ==
              "force_installed" ||
            extensionSettings[extensionID].installation_mode ==
              "normal_installed"
          ) {
799
800
801
802
            if (!extensionSettings[extensionID].install_url) {
              throw new Error(`Missing install_url for ${extensionID}`);
            }
            if (!addons.find(addon => addon.id == extensionID)) {
803
804
805
806
              installAddonFromURL(
                extensionSettings[extensionID].install_url,
                extensionID
              );
807
808
            }
            manager.disallowFeature(`uninstall-extension:${extensionID}`);
809
810
811
812
            if (
              extensionSettings[extensionID].installation_mode ==
              "force_installed"
            ) {
813
814
815
              manager.disallowFeature(`disable-extension:${extensionID}`);
            }
            allowedExtensions.push(extensionID);
816
817
818
          } else if (
            extensionSettings[extensionID].installation_mode == "allowed"
          ) {
819
            allowedExtensions.push(extensionID);
820
821
822
          } else if (
            extensionSettings[extensionID].installation_mode == "blocked"
          ) {
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
            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) {
838
839
840
841
842
          if (
            addon.isSystem ||
            addon.isBuiltin ||
            !(addon.scope & AddonManager.SCOPE_PROFILE)
          ) {
843
844
845
846
847
848
849
850
851
852
853
854
855
856
            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.`);
            }
          }
        }
      }
857
858
859
    },
  },

860
  ExtensionUpdate: {
861
862
863
864
865
866
867
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("extensions.update.enabled", param);
      }
    },
  },

868
  FirefoxHome: {
869
870
871
    onBeforeAddons(manager, param) {
      let locked = param.Locked || false;
      if ("Search" in param) {
872
873
874
875
876
        setDefaultPref(
          "browser.newtabpage.activity-stream.showSearch",
          param.Search,
          locked
        );
877
878
      }
      if ("TopSites" in param) {
879
880
881
882
883
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.topsites",
          param.TopSites,
          locked
        );
884
885
      }
      if ("Highlights" in param) {
886
887
888
889
890
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.highlights",
          param.Highlights,
          locked
        );
891
892
      }
      if ("Pocket" in param) {
893
894
895
896
897
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.topstories",
          param.Pocket,
          locked
        );
898
899
      }
      if ("Snippets" in param) {
900
901
902
903
904
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.snippets",
          param.Snippets,
          locked
        );
905
906
907
908
      }
    },
  },

909
  FlashPlugin: {
910
    onBeforeUIStartup(manager, param) {
911
      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
912
913
914
915
916

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;

      let flashPrefVal;
917
      if (param.Default === undefined || param.Default) {
918
919
920
921
922
923
924
925
926
        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);
      }
927
    },
928
929
  },

930
  HardwareAcceleration: {
931
932
933
934
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("layers.acceleration.disabled", true);
      }
935
    },
936
937
  },

938
  Homepage: {
939
940
941
942
    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.
943
944
      if (param.URL) {
        let homepages = param.URL.href;
945
        if (param.Additional && param.Additional.length) {
946
947
          homepages += "|" + param.Additional.map(url => url.href).join("|");
        }
948
        setDefaultPref("browser.startup.homepage", homepages, param.Locked);
949
        if (param.Locked) {
950
951
952
953
954
955
956
957
958
959
960
961
          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
          );
962
        } else {
963
964
          // Clear out old run once modification that is no longer used.
          clearRunOnceModification("setHomepage");
965
        }
966
      }
967
968
969
970
971
972
973
974
975
976
977
978
979
      if (param.StartPage) {
        let prefValue;
        switch (param.StartPage) {
          case "none":
            prefValue = 0;
            break;
          case "homepage":
            prefValue = 1;
            break;
          case "previous-session":
            prefValue = 3;
            break;
        }
980
        setDefaultPref("browser.startup.page", prefValue, param.Locked);
981
      }
982
    },
983
984
  },

985
  InstallAddonsPermission: {
986
    onBeforeUIStartup(manager, param) {
987
988
989
990
991
      if ("Allow" in param) {
        addAllowDenyPermissions("install", param.Allow, null);
      }
      if ("Default" in param) {
        setAndLockPref("xpinstall.enabled", param.Default);
992
        if (!param.Default) {
993
          blockAboutPage(manager, "about:debugging");
994
995
996
997
998
999
1000
1001
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
            false
          );
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
            false
          );
1002
          manager.disallowFeature("xpinstall");
1003
        }
1004
      }
1005
    },
1006
1007
  },

1008
1009
1010
1011
  LegacyProfiles: {
    // Handled in nsToolkitProfileService.cpp (Windows only)
  },

1012
  LocalFileLinks: {
1013
1014
    onBeforeAddons(manager, param) {
      // If there are existing capabilities, lock them with the policy pref.
1015
1016
1017
      let policyNames = Services.prefs
        .getCharPref("capability.policy.policynames", "")
        .split(" ");
1018
1019
      policyNames.push("localfilelinks_policy");
      setAndLockPref("capability.policy.policynames", policyNames.join(" "));
1020
1021
1022
1023
1024
1025
1026
1027
      setAndLockPref(
        "capability.policy.localfilelinks_policy.checkloaduri.enabled",
        "allAccess"
      );
      setAndLockPref(
        "capability.policy.localfilelinks_policy.sites",
        param.join(" ")
      );
1028
1029
1030
    },
  },

1031
  NetworkPrediction: {
1032
1033
1034
1035
1036
1037
    onBeforeAddons(manager, param) {
      setAndLockPref("network.dns.disablePrefetch", !param);
      setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
    },
  },

1038
  NewTabPage: {
1039
1040
1041
1042
1043
    onBeforeAddons(manager, param) {
      setAndLockPref("browser.newtabpage.enabled", param);
    },
  },

1044
  NoDefaultBookmarks: {
1045
1046
1047
1048
    onProfileAfterChange(manager, param) {
      if (param) {
        manager.disallowFeature("defaultBookmarks"