1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5"use strict"; 6 7const { Preferences } = ChromeUtils.import( 8 "resource://gre/modules/Preferences.jsm" 9); 10const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); 11const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); 12const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm"); 13ChromeUtils.defineModuleGetter( 14 this, 15 "FileTestUtils", 16 "resource://testing-common/FileTestUtils.jsm" 17); 18 19var EXPORTED_SYMBOLS = ["EnterprisePolicyTesting", "PoliciesPrefTracker"]; 20 21var EnterprisePolicyTesting = { 22 // |json| must be an object representing the desired policy configuration, OR a 23 // path to the JSON file containing the policy configuration. 24 setupPolicyEngineWithJson: async function setupPolicyEngineWithJson( 25 json, 26 customSchema 27 ) { 28 let filePath; 29 if (typeof json == "object") { 30 filePath = FileTestUtils.getTempFile("policies.json").path; 31 32 // This file gets automatically deleted by FileTestUtils 33 // at the end of the test run. 34 await OS.File.writeAtomic(filePath, JSON.stringify(json), { 35 encoding: "utf-8", 36 }); 37 } else { 38 filePath = json; 39 } 40 41 Services.prefs.setStringPref("browser.policies.alternatePath", filePath); 42 43 let promise = new Promise(resolve => { 44 Services.obs.addObserver(function observer() { 45 Services.obs.removeObserver( 46 observer, 47 "EnterprisePolicies:AllPoliciesApplied" 48 ); 49 resolve(); 50 }, "EnterprisePolicies:AllPoliciesApplied"); 51 }); 52 53 // Clear any previously used custom schema 54 Cu.unload("resource:///modules/policies/schema.jsm"); 55 56 if (customSchema) { 57 let schemaModule = ChromeUtils.import( 58 "resource:///modules/policies/schema.jsm", 59 null 60 ); 61 schemaModule.schema = customSchema; 62 } 63 64 Services.obs.notifyObservers(null, "EnterprisePolicies:Restart"); 65 return promise; 66 }, 67 68 checkPolicyPref(prefName, expectedValue, expectedLockedness) { 69 if (expectedLockedness !== undefined) { 70 Assert.equal( 71 Preferences.locked(prefName), 72 expectedLockedness, 73 `Pref ${prefName} is correctly locked/unlocked` 74 ); 75 } 76 77 Assert.equal( 78 Preferences.get(prefName), 79 expectedValue, 80 `Pref ${prefName} has the correct value` 81 ); 82 }, 83 84 resetRunOnceState: function resetRunOnceState() { 85 const runOnceBaseKeys = [ 86 "browser.policies.runonce.", 87 "browser.policies.runOncePerModification.", 88 ]; 89 for (let base of runOnceBaseKeys) { 90 for (let key of Services.prefs.getChildList(base)) { 91 if (Services.prefs.prefHasUserValue(key)) { 92 Services.prefs.clearUserPref(key); 93 } 94 } 95 } 96 }, 97}; 98 99/** 100 * This helper will track prefs that have been changed 101 * by the policy engine through the setAndLockPref and 102 * setDefaultPref APIs (from Policies.jsm) and make sure 103 * that they are restored to their original values when 104 * the test ends or another test case restarts the engine. 105 */ 106var PoliciesPrefTracker = { 107 _originalFunc: null, 108 _originalValues: new Map(), 109 110 start() { 111 let PoliciesBackstage = ChromeUtils.import( 112 "resource:///modules/policies/Policies.jsm", 113 null 114 ); 115 this._originalFunc = PoliciesBackstage.setDefaultPref; 116 PoliciesBackstage.setDefaultPref = this.hoistedSetDefaultPref.bind(this); 117 }, 118 119 stop() { 120 this.restoreDefaultValues(); 121 122 let PoliciesBackstage = ChromeUtils.import( 123 "resource:///modules/policies/Policies.jsm", 124 null 125 ); 126 PoliciesBackstage.setDefaultPref = this._originalFunc; 127 this._originalFunc = null; 128 }, 129 130 hoistedSetDefaultPref(prefName, prefValue, locked = false) { 131 // If this pref is seen multiple times, the very first 132 // value seen is the one that is actually the default. 133 if (!this._originalValues.has(prefName)) { 134 let defaults = new Preferences({ defaultBranch: true }); 135 let stored = {}; 136 137 if (defaults.has(prefName)) { 138 stored.originalDefaultValue = defaults.get(prefName); 139 } else { 140 stored.originalDefaultValue = undefined; 141 } 142 143 if ( 144 Preferences.isSet(prefName) && 145 Preferences.get(prefName) == prefValue 146 ) { 147 // If a user value exists, and we're changing the default 148 // value to be th same as the user value, that will cause 149 // the user value to be dropped. In that case, let's also 150 // store it to ensure that we restore everything correctly. 151 stored.originalUserValue = Preferences.get(prefName); 152 } 153 154 this._originalValues.set(prefName, stored); 155 } 156 157 // Now that we've stored the original values, call the 158 // original setDefaultPref function. 159 this._originalFunc(prefName, prefValue, locked); 160 }, 161 162 restoreDefaultValues() { 163 let defaults = new Preferences({ defaultBranch: true }); 164 165 for (let [prefName, stored] of this._originalValues) { 166 // If a pref was used through setDefaultPref instead 167 // of setAndLockPref, it wasn't locked, but calling 168 // unlockPref is harmless 169 Preferences.unlock(prefName); 170 171 if (stored.originalDefaultValue !== undefined) { 172 defaults.set(prefName, stored.originalDefaultValue); 173 } else { 174 Services.prefs.getDefaultBranch("").deleteBranch(prefName); 175 } 176 177 if (stored.originalUserValue !== undefined) { 178 Preferences.set(prefName, stored.originalUserValue); 179 } 180 } 181 182 this._originalValues.clear(); 183 }, 184}; 185