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
        blockAboutPage(manager, "about:profiling");
405
      }
406
    },
407
408
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

590
  DisplayMenuBar: {
591
    onBeforeUIStartup(manager, param) {
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
630
      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;
        }
631
632
633
634
635
636
        gXulStore.setValue(
          BROWSER_DOCUMENT_URL,
          "toolbar-menubar",
          "autohide",
          value
        );
637
638
        manager.disallowFeature("hideShowMenuBar");
      }
639
    },
640
641
  },

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

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

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

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

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

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

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

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

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

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;

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

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

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

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

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

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

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

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

Victor Porof's avatar