/* 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"; const { XPCOMUtils } = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ); ChromeUtils.defineESModuleGetters(this, { schema: "resource:///modules/policies/schema.sys.mjs", }); function col(text, className) { let column = document.createElement("td"); if (className) { column.classList.add(className); } let content = document.createTextNode(text); column.appendChild(content); return column; } function link(text) { let column = document.createElement("td"); let a = document.createElement("a"); a.href = "https://mozilla.github.io/policy-templates/#" + text.toLowerCase(); a.target = "_blank"; let content = document.createTextNode(text); a.appendChild(content); column.appendChild(a); return column; } function addMissingColumns() { const table = document.getElementById("activeContent"); let maxColumns = 0; // count the number of columns per row and set the max number of columns for (let i = 0, length = table.rows.length; i < length; i++) { if (maxColumns < table.rows[i].cells.length) { maxColumns = table.rows[i].cells.length; } } // add the missing columns for (let i = 0, length = table.rows.length; i < length; i++) { const rowLength = table.rows[i].cells.length; if (rowLength < maxColumns) { let missingColumns = maxColumns - rowLength; while (missingColumns > 0) { table.rows[i].insertCell(); missingColumns--; } } } } /* * This function generates the Active Policies content to be displayed by calling * a recursive function called generatePolicy() according to the policy schema. */ function generateActivePolicies(data) { let new_cont = document.getElementById("activeContent"); new_cont.classList.add("active-policies"); let policy_count = 0; for (let policyName in data) { const color_class = ++policy_count % 2 === 0 ? "even" : "odd"; if (schema.properties[policyName].type == "array") { for (let count in data[policyName]) { let isFirstRow = count == 0; let isLastRow = count == data[policyName].length - 1; let row = document.createElement("tr"); row.classList.add(color_class); row.appendChild(col(isFirstRow ? policyName : "")); generatePolicy( data[policyName][count], row, 1, new_cont, isLastRow, data[policyName].length > 1 ); } } else if (schema.properties[policyName].type == "object") { let count = 0; for (let obj in data[policyName]) { let isFirstRow = count == 0; let isLastRow = count == Object.keys(data[policyName]).length - 1; let row = document.createElement("tr"); row.classList.add(color_class); row.appendChild(col(isFirstRow ? policyName : "")); row.appendChild(col(obj)); generatePolicy( data[policyName][obj], row, 2, new_cont, isLastRow, true ); count++; } } else { let row = document.createElement("tr"); row.appendChild(col(policyName)); row.appendChild(col(JSON.stringify(data[policyName]))); row.classList.add(color_class, "last_row"); new_cont.appendChild(row); } } if (policy_count < 1) { let current_tab = document.querySelector(".active"); if (Services.policies.status == Services.policies.ACTIVE) { current_tab.classList.add("no-specified-policies"); } else { current_tab.classList.add("inactive-service"); } } addMissingColumns(); } /* * This is a helper recursive function that iterates levels of each * policy and formats the content to be displayed accordingly. */ function generatePolicy(data, row, depth, new_cont, islast, arr_sep = false) { const color_class = row.classList.contains("odd") ? "odd" : "even"; if (Array.isArray(data)) { for (let count in data) { if (count == 0) { if (count == data.length - 1) { generatePolicy( data[count], row, depth + 1, new_cont, islast ? islast : false, true ); } else { generatePolicy(data[count], row, depth + 1, new_cont, false, false); } } else if (count == data.length - 1) { let last_row = document.createElement("tr"); last_row.classList.add(color_class, "arr_sep"); for (let i = 0; i < depth; i++) { last_row.appendChild(col("")); } generatePolicy( data[count], last_row, depth + 1, new_cont, islast ? islast : false, arr_sep ); } else { let new_row = document.createElement("tr"); new_row.classList.add(color_class); for (let i = 0; i < depth; i++) { new_row.appendChild(col("")); } generatePolicy(data[count], new_row, depth + 1, new_cont, false, false); } } } else if (typeof data == "object" && Object.keys(data).length) { let count = 0; for (let obj in data) { if (count == 0) { row.appendChild(col(obj)); if (count == Object.keys(data).length - 1) { generatePolicy( data[obj], row, depth + 1, new_cont, islast ? islast : false, arr_sep ); } else { generatePolicy(data[obj], row, depth + 1, new_cont, false, false); } } else if (count == Object.keys(data).length - 1) { let last_row = document.createElement("tr"); for (let i = 0; i < depth; i++) { last_row.appendChild(col("")); } last_row.appendChild(col(obj)); last_row.classList.add(color_class); if (arr_sep) { last_row.classList.add("arr_sep"); } generatePolicy( data[obj], last_row, depth + 1, new_cont, islast ? islast : false, false ); } else { let new_row = document.createElement("tr"); new_row.classList.add(color_class); for (let i = 0; i < depth; i++) { new_row.appendChild(col("")); } new_row.appendChild(col(obj)); generatePolicy(data[obj], new_row, depth + 1, new_cont, false, false); } count++; } } else { row.appendChild(col(JSON.stringify(data))); if (arr_sep) { row.classList.add("arr_sep"); } if (islast) { row.classList.add("last_row"); } new_cont.appendChild(row); } } function generateErrors() { const consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"]; const storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage); const consoleEvents = storage.getEvents(); const prefixes = [ "Enterprise Policies", "JsonSchemaValidator.jsm", "Policies.jsm", "GPOParser.jsm", "Enterprise Policies Child", "BookmarksPolicies.jsm", "ProxyPolicies.jsm", "WebsiteFilter Policy", "macOSPoliciesParser.jsm", ]; let new_cont = document.getElementById("errorsContent"); new_cont.classList.add("errors"); let flag = false; for (let err of consoleEvents) { if (prefixes.includes(err.prefix)) { flag = true; let row = document.createElement("tr"); row.appendChild(col(err.arguments[0])); new_cont.appendChild(row); } } if (!flag) { let errors_tab = document.getElementById("category-errors"); errors_tab.style.display = "none"; } } function generateDocumentation() { let new_cont = document.getElementById("documentationContent"); new_cont.setAttribute("id", "documentationContent"); // map specific policies to a different string ID, to allow updates to // existing descriptions let string_mapping = { BackgroundAppUpdate: "BackgroundAppUpdate2", Certificates: "CertificatesDescription", DisableMasterPasswordCreation: "DisablePrimaryPasswordCreation", DisablePocket: "DisablePocket2", DisableSetDesktopBackground: "DisableSetAsDesktopBackground", FirefoxHome: "FirefoxHome2", Permissions: "Permissions2", SanitizeOnShutdown: "SanitizeOnShutdown2", WindowsSSO: "Windows10SSO", SecurityDevices: "SecurityDevices2", }; for (let policyName in schema.properties) { let main_tbody = document.createElement("tbody"); main_tbody.classList.add("collapsible"); main_tbody.addEventListener("click", function () { let content = this.nextElementSibling; content.classList.toggle("content"); }); let row = document.createElement("tr"); row.appendChild(link(policyName)); let descriptionColumn = col(""); let stringID = string_mapping[policyName] || policyName; descriptionColumn.setAttribute("data-l10n-id", `policy-${stringID}`); row.appendChild(descriptionColumn); main_tbody.appendChild(row); let sec_tbody = document.createElement("tbody"); sec_tbody.classList.add("content"); sec_tbody.classList.add("content-style"); let schema_row = document.createElement("tr"); if (schema.properties[policyName].properties) { let column = col( JSON.stringify(schema.properties[policyName].properties, null, 1), "schema" ); column.colSpan = "2"; schema_row.appendChild(column); sec_tbody.appendChild(schema_row); } else if (schema.properties[policyName].items) { let column = col( JSON.stringify(schema.properties[policyName], null, 1), "schema" ); column.colSpan = "2"; schema_row.appendChild(column); sec_tbody.appendChild(schema_row); } else { let column = col("type: " + schema.properties[policyName].type, "schema"); column.colSpan = "2"; schema_row.appendChild(column); sec_tbody.appendChild(schema_row); if (schema.properties[policyName].enum) { let enum_row = document.createElement("tr"); column = col( "enum: " + JSON.stringify(schema.properties[policyName].enum, null, 1), "schema" ); column.colSpan = "2"; enum_row.appendChild(column); sec_tbody.appendChild(enum_row); } } new_cont.appendChild(main_tbody); new_cont.appendChild(sec_tbody); } } let gInited = false; window.onload = function () { if (gInited) { return; } gInited = true; let data = Services.policies.getActivePolicies(); generateActivePolicies(data); generateErrors(); generateDocumentation(); // Event delegation on #categories element let menu = document.getElementById("categories"); for (let category of menu.children) { category.addEventListener("click", () => show(category)); category.addEventListener("keypress", function (event) { if (event.keyCode == KeyEvent.DOM_VK_RETURN) { show(category); } }); } if (location.hash) { let sectionButton = document.getElementById( "category-" + location.hash.substring(1) ); if (sectionButton) { sectionButton.click(); } } window.addEventListener("hashchange", function () { if (location.hash) { let sectionButton = document.getElementById( "category-" + location.hash.substring(1) ); sectionButton.click(); } }); }; function show(button) { let current_tab = document.querySelector(".active"); let category = button.getAttribute("id").substring("category-".length); let content = document.getElementById(category); if (current_tab == content) { return; } saveScrollPosition(current_tab.id); current_tab.classList.remove("active"); current_tab.hidden = true; content.classList.add("active"); content.hidden = false; let current_button = document.querySelector("[selected=true]"); current_button.removeAttribute("selected"); button.setAttribute("selected", "true"); let title = document.getElementById("sectionTitle"); title.textContent = button.textContent; location.hash = category; restoreScrollPosition(category); } const scrollPositions = {}; function saveScrollPosition(category) { const mainContent = document.querySelector(".main-content"); scrollPositions[category] = mainContent.scrollTop; } function restoreScrollPosition(category) { const scrollY = scrollPositions[category] || 0; const mainContent = document.querySelector(".main-content"); mainContent.scrollTo(0, scrollY); }