Policies.jsm 51.1 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
454
455
456
457
458
459
460
  DisablePasswordReveal: {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("passwordReveal");
      }
    },
  },

461
  DisablePocket: {
462
463
464
465
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.pocket.enabled", false);
      }
466
    },
467
468
  },

469
  DisablePrivateBrowsing: {
470
471
472
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("privatebrowsing");
473
        blockAboutPage(manager, "about:privatebrowsing", true);
474
475
        setAndLockPref("browser.privatebrowsing.autostart", false);
      }
476
    },
477
478
  },

479
  DisableProfileImport: {
480
481
482
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileImport");
483
484
485
486
        setAndLockPref(
          "browser.newtabpage.activity-stream.migrationExpired",
          true
        );
487
      }
488
    },
489
490
  },

491
  DisableProfileRefresh: {
492
493
494
495
496
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileRefresh");
        setAndLockPref("browser.disableResetPrompt", true);
      }
497
    },
498
499
  },

500
  DisableSafeMode: {
501
502
503
504
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("safeMode");
      }
505
    },
506
507
  },

508
  DisableSecurityBypass: {
509
    onBeforeUIStartup(manager, param) {
510
      if ("InvalidCertificate" in param) {
511
512
513
514
        setAndLockPref(
          "security.certerror.hideAddException",
          param.InvalidCertificate
        );
515
516
      }

517
      if ("SafeBrowsing" in param) {
518
519
520
521
        setAndLockPref(
          "browser.safebrowsing.allowOverride",
          !param.SafeBrowsing
        );
522
      }
523
    },
524
525
  },

526
  DisableSetDesktopBackground: {
527
528
    onBeforeUIStartup(manager, param) {
      if (param) {
529
        manager.disallowFeature("setDesktopBackground");
530
      }
531
    },
532
533
  },

534
  DisableSystemAddonUpdate: {
535
536
537
538
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("SysAddonUpdate");
      }
539
    },
540
541
  },

542
  DisableTelemetry: {
543
544
545
546
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
547
        blockAboutPage(manager, "about:telemetry");
548
      }
549
    },
550
551
  },

552
  DisplayBookmarksToolbar: {
553
    onBeforeUIStartup(manager, param) {
554
555
556
557
558
      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, () => {
559
560
561
562
563
564
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "PersonalToolbar",
          "collapsed",
          value
        );
565
      });
566
    },
567
568
  },

569
  DisplayMenuBar: {
570
    onBeforeUIStartup(manager, param) {
571
      let value = (!param).toString();
572
573
574
      // 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.
575
      runOncePerModification("displayMenuBar", value, () => {
576
577
578
579
580
581
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "toolbar-menubar",
          "autohide",
          value
        );
582
      });
583
    },
584
585
  },

586
  DNSOverHTTPS: {
587
588
589
    onBeforeAddons(manager, param) {
      if ("Enabled" in param) {
        let mode = param.Enabled ? 2 : 5;
590
        setDefaultPref("network.trr.mode", mode, param.Locked);
591
592
      }
      if (param.ProviderURL) {
593
        setDefaultPref("network.trr.uri", param.ProviderURL.href, param.Locked);
594
595
596
597
      }
    },
  },

598
  DontCheckDefaultBrowser: {
599
    onBeforeUIStartup(manager, param) {
600
      setAndLockPref("browser.shell.checkDefaultBrowser", !param);
601
    },
602
603
  },

604
  DownloadDirectory: {
605
606
607
608
    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);
609
610
611
      // Per Chrome spec, user can't choose to download every time
      // if this is set.
      setAndLockPref("browser.download.useDownloadDir", true);
612
613
614
    },
  },

615
  EnableTrackingProtection: {
616
    onBeforeUIStartup(manager, param) {
617
      if (param.Value) {
618
619
620
621
622
623
624
625
626
627
        setDefaultPref(
          "privacy.trackingprotection.enabled",
          true,
          param.Locked
        );
        setDefaultPref(
          "privacy.trackingprotection.pbmode.enabled",
          true,
          param.Locked
        );
628
      } else {
629
630
        setAndLockPref("privacy.trackingprotection.enabled", false);
        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
631
      }
632
633
634
635
636
637
638
639
640
641
642
643
644
645
      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
        );
      }
646
    },
647
648
  },

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

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

805
  ExtensionUpdate: {
806
807
808
809
810
811
812
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("extensions.update.enabled", param);
      }
    },
  },

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

854
  FlashPlugin: {
855
    onBeforeUIStartup(manager, param) {
856
      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
857
858
859
860
861

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;

      let flashPrefVal;
862
      if (param.Default === undefined || param.Default) {
863
864
865
866
867
868
869
870
871
        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);
      }
872
    },
873
874
  },

875
  HardwareAcceleration: {
876
877
878
879
    onBeforeAddons(manager, param) {
      if (!param) {
        setAndLockPref("layers.acceleration.disabled", true);
      }
880
    },
881
882
  },

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

930
  InstallAddonsPermission: {
931
    onBeforeUIStartup(manager, param) {
932
933
934
935
936
      if ("Allow" in param) {
        addAllowDenyPermissions("install", param.Allow, null);
      }
      if ("Default" in param) {
        setAndLockPref("xpinstall.enabled", param.Default);
937
        if (!param.Default) {
938
          blockAboutPage(manager, "about:debugging");
939
940
941
942
943
944
945
946
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
            false
          );
          setAndLockPref(
            "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
            false
          );
947
          manager.disallowFeature("xpinstall");
948
        }
949
      }
950
    },
951
952
  },

953
954
955
956
  LegacyProfiles: {
    // Handled in nsToolkitProfileService.cpp (Windows only)
  },

957
  LocalFileLinks: {
958
959
    onBeforeAddons(manager, param) {
      // If there are existing capabilities, lock them with the policy pref.
960
961
962
      let policyNames = Services.prefs
        .getCharPref("capability.policy.policynames", "")
        .split(" ");
963
964
      policyNames.push("localfilelinks_policy");
      setAndLockPref("capability.policy.policynames", policyNames.join(" "));
965
966
967
968
969
970
971
972
      setAndLockPref(
        "capability.policy.localfilelinks_policy.checkloaduri.enabled",
        "allAccess"
      );
      setAndLockPref(
        "capability.policy.localfilelinks_policy.sites",
        param.join(" ")
      );
973
974
975
    },
  },

976
  NetworkPrediction: {
977
978
979
980
981
982
    onBeforeAddons(manager, param) {
      setAndLockPref("network.dns.disablePrefetch", !param);
      setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
    },
  },

983
  NewTabPage: {
984
985
986
987
988
    onBeforeAddons(manager, param) {
      setAndLockPref("browser.newtabpage.enabled", param);
    },
  },

989
  NoDefaultBookmarks: {
990
991
992
993
    onProfileAfterChange(manager, param) {
      if (param) {
        manager.disallowFeature("defaultBookmarks");
      }
994
    },
995
996
  },

997
  OfferToSaveLogins: {
998
999
    onBeforeUIStartup(manager, param) {
      setAndLockPref("signon.rememberSignons", param);
1000
    },
1001
1002
  },

1003
1004
1005
1006
1007
1008
  OfferToSaveLoginsDefault: {
    onBeforeUIStartup(manager, param) {
      setDefaultPref("signon.rememberSignons", param);
    },
  },

1009
  OverrideFirstRunPage: {
1010
    onProfileAfterChange(manager, param) {
1011
      let url = param ? param.href : "";
Felipe Gomes's avatar