Policies.jsm 50.9 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
115
      if ("AllowNonFQDN" in param) {
        if (param.AllowNonFQDN.NTLM) {
116
          setDefaultPref(
117
            "network.automatic-ntlm-auth.allow-non-fqdn",
118
119
            param.AllowNonFQDN.NTLM,
            locked
120
          );
121
122
        }
        if (param.AllowNonFQDN.SPNEGO) {
123
          setDefaultPref(
124
            "network.negotiate-auth.allow-non-fqdn",
125
126
            param.AllowNonFQDN.SPNEGO,
            locked
127
          );
128
129
        }
      }
130
    },
131
132
  },

133
  BlockAboutAddons: {
134
135
    onBeforeUIStartup(manager, param) {
      if (param) {
136
        blockAboutPage(manager, "about:addons", true);
137
      }
138
    },
139
140
  },

141
  BlockAboutConfig: {
142
    onBeforeUIStartup(manager, param) {
143
      if (param) {
144
        blockAboutPage(manager, "about:config");
145
        setAndLockPref("devtools.chrome.enabled", false);
146
      }
147
    },
148
  },
149

150
  BlockAboutProfiles: {
151
152
    onBeforeUIStartup(manager, param) {
      if (param) {
153
        blockAboutPage(manager, "about:profiles");
154
      }
155
    },
156
157
  },

158
  BlockAboutSupport: {
159
160
    onBeforeUIStartup(manager, param) {
      if (param) {
161
        blockAboutPage(manager, "about:support");
162
      }
163
    },
164
165
  },

166
  Bookmarks: {
167
168
    onAllWindowsRestored(manager, param) {
      BookmarksPolicies.processBookmarks(param);
169
    },
170
171
  },

172
  CaptivePortal: {
173
174
175
176
177
    onBeforeAddons(manager, param) {
      setAndLockPref("network.captive-portal-service.enabled", param);
    },
  },

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

282
  Cookies: {
283
    onBeforeUIStartup(manager, param) {
284
      addAllowDenyPermissions("cookie", param.Allow, param.Block);
285
286

      if (param.Block) {
287
288
289
        const hosts = param.Block.map(url => url.hostname)
          .sort()
          .join("\n");
290
291
        runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
          for (let blocked of param.Block) {
292
293
294
295
            Services.cookies.removeCookiesWithOriginAttributes(
              "{}",
              blocked.hostname
            );
296
297
298
          }
        });
      }
299

300
301
302
303
304
305
      if (
        param.Default !== undefined ||
        param.AcceptThirdParty !== undefined ||
        param.RejectTracker !== undefined ||
        param.Locked
      ) {
306
307
308
309
        const ACCEPT_COOKIES = 0;
        const REJECT_THIRD_PARTY_COOKIES = 1;
        const REJECT_ALL_COOKIES = 2;
        const REJECT_UNVISITED_THIRD_PARTY = 3;
310
        const REJECT_TRACKER = 4;
311
312
313
314
315

        let newCookieBehavior = ACCEPT_COOKIES;
        if (param.Default !== undefined && !param.Default) {
          newCookieBehavior = REJECT_ALL_COOKIES;
        } else if (param.AcceptThirdParty) {
316
          if (param.AcceptThirdParty == "never") {
317
318
319
320
            newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
          } else if (param.AcceptThirdParty == "from-visited") {
            newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
          }
321
322
        } else if (param.RejectTracker !== undefined && param.RejectTracker) {
          newCookieBehavior = REJECT_TRACKER;
323
324
        }

325
326
327
328
329
        setDefaultPref(
          "network.cookie.cookieBehavior",
          newCookieBehavior,
          param.Locked
        );
330
331
332
333
334
335
336
337
338
339
340
      }

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

341
342
343
344
345
        setDefaultPref(
          "network.cookie.lifetimePolicy",
          newLifetimePolicy,
          param.Locked
        );
346
      }
347
    },
348
349
  },

350
  DefaultDownloadDirectory: {
351
352
353
354
355
356
357
    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);
    },
  },

358
  DisableAppUpdate: {
359
360
361
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("appUpdate");
362
      }
363
    },
364
365
  },

366
  DisableBuiltinPDFViewer: {
367
    onBeforeAddons(manager, param) {
368
      if (param) {
369
        setAndLockPref("pdfjs.disabled", true);
370
      }
371
    },
372
373
  },

374
  DisableDeveloperTools: {
375
376
377
378
379
380
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("devtools.policy.disabled", true);
        setAndLockPref("devtools.chrome.enabled", false);

        manager.disallowFeature("devtools");
381
382
383
        blockAboutPage(manager, "about:devtools");
        blockAboutPage(manager, "about:debugging");
        blockAboutPage(manager, "about:devtools-toolbox");
384
      }
385
    },
386
387
  },

388
  DisableFeedbackCommands: {
389
390
391
392
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("feedbackCommands");
      }
393
    },
394
395
  },

396
  DisableFirefoxAccounts: {
397
398
399
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("identity.fxaccounts.enabled", false);
400
        setAndLockPref("trailhead.firstrun.branches", "nofirstrun");
401
      }
402
    },
403
404
  },

405
  DisableFirefoxScreenshots: {
406
    onBeforeAddons(manager, param) {
407
      if (param) {
408
409
        setAndLockPref("extensions.screenshots.disabled", true);
      }
410
    },
411
412
  },

413
  DisableFirefoxStudies: {
414
    onBeforeAddons(manager, param) {
415
      if (param) {
416
        manager.disallowFeature("Shield");
417
418
419
420
421
422
423
424
        setAndLockPref(
          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
          false
        );
        setAndLockPref(
          "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
          false
        );
425
      }
426
    },
427
428
  },

429
  DisableForgetButton: {
430
431
    onProfileAfterChange(manager, param) {
      if (param) {
432
        setAndLockPref("privacy.panicButton.enabled", false);
433
      }
434
    },
435
436
  },

437
  DisableFormHistory: {
438
    onBeforeUIStartup(manager, param) {
439
      if (param) {
440
441
        setAndLockPref("browser.formfill.enable", false);
      }
442
    },
443
444
  },

445
  DisableMasterPasswordCreation: {
446
447
448
449
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("createMasterPassword");
      }
450
    },
451
452
  },

453
  DisablePocket: {
454
455
456
457
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.pocket.enabled", false);
      }
458
    },
459
460
  },

461
  DisablePrivateBrowsing: {
462
463
464
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("privatebrowsing");
465
        blockAboutPage(manager, "about:privatebrowsing", true);
466
467
        setAndLockPref("browser.privatebrowsing.autostart", false);
      }
468
    },
469
470
  },

471
  DisableProfileImport: {
472
473
474
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileImport");
475
476
477
478
        setAndLockPref(
          "browser.newtabpage.activity-stream.migrationExpired",
          true
        );
479
      }
480
    },
481
482
  },

483
  DisableProfileRefresh: {
484
485
486
487
488
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileRefresh");
        setAndLockPref("browser.disableResetPrompt", true);
      }
489
    },
490
491
  },

492
  DisableSafeMode: {
493
494
495
496
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("safeMode");
      }
497
    },
498
499
  },

500
  DisableSecurityBypass: {
501
    onBeforeUIStartup(manager, param) {
502
      if ("InvalidCertificate" in param) {
503
504
505
506
        setAndLockPref(
          "security.certerror.hideAddException",
          param.InvalidCertificate
        );
507
508
      }

509
      if ("SafeBrowsing" in param) {
510
511
512
513
        setAndLockPref(
          "browser.safebrowsing.allowOverride",
          !param.SafeBrowsing
        );
514
      }
515
    },
516
517
  },

518
  DisableSetDesktopBackground: {
519
520
    onBeforeUIStartup(manager, param) {
      if (param) {
521
        manager.disallowFeature("setDesktopBackground");
522
      }
523
    },
524
525
  },

526
  DisableSystemAddonUpdate: {
527
528
529
530
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("SysAddonUpdate");
      }
531
    },
532
533
  },

534
  DisableTelemetry: {
535
536
537
538
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
539
        blockAboutPage(manager, "about:telemetry");
540
      }
541
    },
542
543
  },

544
  DisplayBookmarksToolbar: {
545
    onBeforeUIStartup(manager, param) {
546
547
548
549
550
      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, () => {
551
552
553
554
555
556
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "PersonalToolbar",
          "collapsed",
          value
        );
557
      });
558
    },
559
560
  },

561
  DisplayMenuBar: {
562
    onBeforeUIStartup(manager, param) {
563
      let value = (!param).toString();
564
565
566
      // 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.
567
      runOncePerModification("displayMenuBar", value, () => {
568
569
570
571
572
573
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "toolbar-menubar",
          "autohide",
          value
        );
574
      });
575
    },
576
577
  },

578
  DNSOverHTTPS: {
579
580
581
    onBeforeAddons(manager, param) {
      if ("Enabled" in param) {
        let mode = param.Enabled ? 2 : 5;
582
        setDefaultPref("network.trr.mode", mode, param.Locked);
583
584
      }
      if (param.ProviderURL) {
585
        setDefaultPref("network.trr.uri", param.ProviderURL.href, param.Locked);
586
587
588
589
      }
    },
  },

590
  DontCheckDefaultBrowser: {
591
    onBeforeUIStartup(manager, param) {
592
      setAndLockPref("browser.shell.checkDefaultBrowser", !param);
593
    },
594
595
  },

596
  DownloadDirectory: {
597
598
599
600
    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);
601
602
603
      // Per Chrome spec, user can't choose to download every time
      // if this is set.
      setAndLockPref("browser.download.useDownloadDir", true);
604
605
606
    },
  },

607
  EnableTrackingProtection: {
608
    onBeforeUIStartup(manager, param) {
609
      if (param.Value) {
610
611
612
613
614
615
616
617
618
619
        setDefaultPref(
          "privacy.trackingprotection.enabled",
          true,
          param.Locked
        );
        setDefaultPref(
          "privacy.trackingprotection.pbmode.enabled",
          true,
          param.Locked
        );
620
      } else {
621
622
        setAndLockPref("privacy.trackingprotection.enabled", false);
        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
623
      }
624
625
626
627
628
629
630
631
632
633
634
635
636
637
      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
        );
      }
638
    },
639
640
  },

641
  Extensions: {
642
    onBeforeUIStartup(manager, param) {
643
644
      let uninstallingPromise = Promise.resolve();
      if ("Uninstall" in param) {
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
        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.`);
                }
663
664
665
              }
            }
          }
666
        );
667
      }
668
      if ("Install" in param) {
669
670
671
672
673
674
675
        runOncePerModification(
          "extensionsInstall",
          JSON.stringify(param.Install),
          async () => {
            await uninstallingPromise;
            for (let location of param.Install) {
              let uri;
676
              try {
677
678
                // We need to try as a file first because
                // Windows paths are valid URIs.
679
                // This is done for legacy support (old API)
680
681
682
683
                let xpiFile = new FileUtils.File(location);
                uri = Services.io.newFileURI(xpiFile);
              } catch (e) {
                uri = Services.io.newURI(location);
684
              }
685
              installAddonFromURL(uri.spec);
686
            }
687
          }
688
        );
689
690
691
      }
      if ("Locked" in param) {
        for (let ID of param.Locked) {
692
693
          manager.disallowFeature(`uninstall-extension:${ID}`);
          manager.disallowFeature(`disable-extension:${ID}`);
694
695
        }
      }
696
    },
697
698
  },

699
  ExtensionSettings: {
700
    onBeforeAddons(manager, param) {
701
702
703
      try {
        manager.setExtensionSettings(param);
      } catch (e) {
704
        log.error("Invalid ExtensionSettings");
705
706
707
708
709
710
      }
    },
    async onBeforeUIStartup(manager, param) {
      let extensionSettings = param;
      let blockAllExtensions = false;
      if ("*" in extensionSettings) {
711
712
713
714
        if (
          "installation_mode" in extensionSettings["*"] &&
          extensionSettings["*"].installation_mode == "blocked"
        ) {
715
716
717
718
719
720
721
          blockAllExtensions = true;
          // Turn off discovery pane in about:addons
          setAndLockPref("extensions.getAddons.showPane", false);
          // Block about:debugging
          blockAboutPage(manager, "about:debugging");
        }
      }
722
      let { addons } = await AddonManager.getActiveAddons();
723
724
725
726
727
728
729
      let allowedExtensions = [];
      for (let extensionID in extensionSettings) {
        if (extensionID == "*") {
          // Ignore global settings
          continue;
        }
        if ("installation_mode" in extensionSettings[extensionID]) {
730
731
732
733
734
735
          if (
            extensionSettings[extensionID].installation_mode ==
              "force_installed" ||
            extensionSettings[extensionID].installation_mode ==
              "normal_installed"
          ) {
736
737
738
739
            if (!extensionSettings[extensionID].install_url) {
              throw new Error(`Missing install_url for ${extensionID}`);
            }
            if (!addons.find(addon => addon.id == extensionID)) {
740
741
742
743
              installAddonFromURL(
                extensionSettings[extensionID].install_url,
                extensionID
              );
744
745
            }
            manager.disallowFeature(`uninstall-extension:${extensionID}`);
746
747
748
749
            if (
              extensionSettings[extensionID].installation_mode ==
              "force_installed"
            ) {
750
751
752
              manager.disallowFeature(`disable-extension:${extensionID}`);
            }
            allowedExtensions.push(extensionID);
753
754
755
          } else if (
            extensionSettings[extensionID].installation_mode == "allowed"
          ) {
756
            allowedExtensions.push(extensionID);
757
758
759
          } else if (
            extensionSettings[extensionID].installation_mode == "blocked"
          ) {
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
            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) {
775
776
777
778
779
          if (
            addon.isSystem ||
            addon.isBuiltin ||
            !(addon.scope & AddonManager.SCOPE_PROFILE)
          ) {
780
781
782
783
784
785
786
787
788
789
790
791
792
793
            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.`);
            }
          }
        }
      }
794
795
796
    },
  },

797
  ExtensionUpdate: {
798
799
800
801
802
803
804
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("extensions.update.enabled", param);
      }
    },
  },

805
  FirefoxHome: {
806
807
808
    onBeforeAddons(manager, param) {
      let locked = param.Locked || false;
      if ("Search" in param) {
809
810
811
812
813
        setDefaultPref(
          "browser.newtabpage.activity-stream.showSearch",
          param.Search,
          locked
        );
814
815
      }
      if ("TopSites" in param) {
816
817
818
819
820
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.topsites",
          param.TopSites,
          locked
        );
821
822
      }
      if ("Highlights" in param) {
823
824
825
826
827
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.highlights",
          param.Highlights,
          locked
        );
828
829
      }
      if ("Pocket" in param) {
830
831
832
833
834
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.section.topstories",
          param.Pocket,
          locked
        );
835
836
      }
      if ("Snippets" in param) {
837
838
839
840
841
        setDefaultPref(
          "browser.newtabpage.activity-stream.feeds.snippets",
          param.Snippets,
          locked
        );
842
843
844
845
      }
    },
  },

846
  FlashPlugin: {
847
    onBeforeUIStartup(manager, param) {
848
      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
849
850
851
852
853

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;

      let flashPrefVal;
854
      if (param.Default === undefined || param.Default) {
855
856
857
858
859
860
861
862
863
        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);
      }
864
    },
865
866
  },

867
  HardwareAcceleration: {
868
869
870
871
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("layers.acceleration.disabled", true);
      }
872
    },
873
874
  },

875
  Homepage: {
876
877
878
879
    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.
880
881
      if (param.URL) {
        let homepages = param.URL.href;
882
        if (param.Additional && param.Additional.length) {
883
884
          homepages += "|" + param.Additional.map(url => url.href).join("|");
        }
885
        setDefaultPref("browser.startup.homepage", homepages, param.Locked);
886
        if (param.Locked) {
887
888
889
890
891
892
893
894
895
896
897
898
          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
          );
899
        } else {
900
901
          // Clear out old run once modification that is no longer used.
          clearRunOnceModification("setHomepage");
902
        }
903
      }
904
905
906
907
908
909
910
911
912
913
914
915
916
      if (param.StartPage) {
        let prefValue;
        switch (param.StartPage) {
          case "none":
            prefValue = 0;
            break;
          case "homepage":
            prefValue = 1;
            break;
          case "previous-session":
            prefValue = 3;
            break;
        }
917
        setDefaultPref("browser.startup.page", prefValue, param.Locked);
918
      }
919
    },
920
921
  },

922
  InstallAddonsPermission: {
923
    onBeforeUIStartup(manager, param) {
924
925
926
927
928
      if ("Allow" in param) {
        addAllowDenyPermissions("install", param.Allow, null);
      }
      if ("Default" in param) {
        setAndLockPref("xpinstall.enabled", param.Default);
929
        if (!param.Default) {
930
          blockAboutPage(manager, "about:debugging");
931
932
933
934
935
936
937
938
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
            false
          );
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
            false
          );
939
          manager.disallowFeature("xpinstall");
940
        }
941
      }
942
    },
943
944
  },

945
946
947
948
  LegacyProfiles: {
    // Handled in nsToolkitProfileService.cpp (Windows only)
  },

949
  LocalFileLinks: {
950
951
    onBeforeAddons(manager, param) {
      // If there are existing capabilities, lock them with the policy pref.
952
953
954
      let policyNames = Services.prefs
        .getCharPref("capability.policy.policynames", "")
        .split(" ");
955
956
      policyNames.push("localfilelinks_policy");
      setAndLockPref("capability.policy.policynames", policyNames.join(" "));
957
958
959
960
961
962
963
964
      setAndLockPref(
        "capability.policy.localfilelinks_policy.checkloaduri.enabled",
        "allAccess"
      );
      setAndLockPref(
        "capability.policy.localfilelinks_policy.sites",
        param.join(" ")
      );
965
966
967
    },
  },

968
  NetworkPrediction: {
969
970
971
972
973
974
    onBeforeAddons(manager, param) {
      setAndLockPref("network.dns.disablePrefetch", !param);
      setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
    },
  },

975
  NewTabPage: {
976
977
978
979
980
    onBeforeAddons(manager, param) {
      setAndLockPref("browser.newtabpage.enabled", param);
    },
  },

981
  NoDefaultBookmarks: {
982
983
984
985
    onProfileAfterChange(manager, param) {
      if (param) {
        manager.disallowFeature("defaultBookmarks");
      }
986
    },
987
988
  },

989
  OfferToSaveLogins: {
990
991
    onBeforeUIStartup(manager, param) {
      setAndLockPref("signon.rememberSignons", param);
992
    },
993
994
  },

995
996
997
998
999
1000
  OfferToSaveLoginsDefault: {
    onBeforeUIStartup(manager, param) {
      setDefaultPref("signon.rememberSignons", param);
    },
  },

1001
  OverrideFirstRunPage: {
1002
    onProfileAfterChange(manager, param) {
1003
      let url = param ? param.href : "";
1004
      setAndLockPref("startup.homepage_welcome_url", url);
1005
      setAndLockPref("trailhead.firstrun.branches", "nofirstrun");
Mark Banner's avatar