diff options
Diffstat (limited to 'toolkit/content/aboutProfiles.js')
-rw-r--r-- | toolkit/content/aboutProfiles.js | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/toolkit/content/aboutProfiles.js b/toolkit/content/aboutProfiles.js new file mode 100644 index 0000000000..15c0419a11 --- /dev/null +++ b/toolkit/content/aboutProfiles.js @@ -0,0 +1,397 @@ +/* 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" +); + +XPCOMUtils.defineLazyServiceGetter( + this, + "ProfileService", + "@mozilla.org/toolkit/profile-service;1", + "nsIToolkitProfileService" +); + +async function flush() { + try { + ProfileService.flush(); + rebuildProfileList(); + } catch (e) { + let [title, msg, button] = await document.l10n.formatValues([ + { id: "profiles-flush-fail-title" }, + { + id: + e.result == Cr.NS_ERROR_DATABASE_CHANGED + ? "profiles-flush-conflict" + : "profiles-flush-failed", + }, + { id: "profiles-flush-restart-button" }, + ]); + + const PS = Ci.nsIPromptService; + let result = Services.prompt.confirmEx( + window, + title, + msg, + PS.BUTTON_POS_0 * PS.BUTTON_TITLE_CANCEL + + PS.BUTTON_POS_1 * PS.BUTTON_TITLE_IS_STRING, + null, + button, + null, + null, + {} + ); + if (result == 1) { + restart(false); + } + } +} + +function rebuildProfileList() { + let parent = document.getElementById("profiles"); + while (parent.firstChild) { + parent.firstChild.remove(); + } + + let defaultProfile; + try { + defaultProfile = ProfileService.defaultProfile; + } catch (e) {} + + let currentProfile = ProfileService.currentProfile; + + for (let profile of ProfileService.profiles) { + let isCurrentProfile = profile == currentProfile; + let isInUse = isCurrentProfile; + if (!isInUse) { + try { + let lock = profile.lock({}); + lock.unlock(); + } catch (e) { + if ( + e.result != Cr.NS_ERROR_FILE_NOT_DIRECTORY && + e.result != Cr.NS_ERROR_FILE_NOT_FOUND + ) { + isInUse = true; + } + } + } + display({ + profile, + isDefault: profile == defaultProfile, + isCurrentProfile, + isInUse, + }); + } +} + +function display(profileData) { + let parent = document.getElementById("profiles"); + + let div = document.createElement("div"); + parent.appendChild(div); + + let name = document.createElement("h2"); + + div.appendChild(name); + document.l10n.setAttributes(name, "profiles-name", { + name: profileData.profile.name, + }); + + if (profileData.isCurrentProfile) { + let currentProfile = document.createElement("h3"); + document.l10n.setAttributes(currentProfile, "profiles-current-profile"); + div.appendChild(currentProfile); + } else if (profileData.isInUse) { + let currentProfile = document.createElement("h3"); + document.l10n.setAttributes(currentProfile, "profiles-in-use-profile"); + div.appendChild(currentProfile); + } + + let table = document.createElement("table"); + div.appendChild(table); + + let tbody = document.createElement("tbody"); + table.appendChild(tbody); + + function createItem(title, value, dir = false) { + let tr = document.createElement("tr"); + tbody.appendChild(tr); + + let th = document.createElement("th"); + th.setAttribute("class", "column"); + document.l10n.setAttributes(th, title); + tr.appendChild(th); + + let td = document.createElement("td"); + tr.appendChild(td); + + if (dir) { + td.appendChild(document.createTextNode(value.path)); + + if (value.exists()) { + let button = document.createElement("button"); + button.setAttribute("class", "opendir"); + document.l10n.setAttributes(button, "profiles-opendir"); + + td.appendChild(button); + + button.addEventListener("click", function (e) { + value.reveal(); + }); + } + } else { + document.l10n.setAttributes(td, value); + } + } + + createItem( + "profiles-is-default", + profileData.isDefault ? "profiles-yes" : "profiles-no" + ); + + createItem("profiles-rootdir", profileData.profile.rootDir, true); + + if (profileData.profile.localDir.path != profileData.profile.rootDir.path) { + createItem("profiles-localdir", profileData.profile.localDir, true); + } + + let renameButton = document.createElement("button"); + document.l10n.setAttributes(renameButton, "profiles-rename"); + renameButton.onclick = function () { + renameProfile(profileData.profile); + }; + div.appendChild(renameButton); + + if (!profileData.isInUse) { + let removeButton = document.createElement("button"); + document.l10n.setAttributes(removeButton, "profiles-remove"); + removeButton.onclick = function () { + removeProfile(profileData.profile); + }; + + div.appendChild(removeButton); + } + + if (!profileData.isDefault) { + let defaultButton = document.createElement("button"); + document.l10n.setAttributes(defaultButton, "profiles-set-as-default"); + defaultButton.onclick = function () { + defaultProfile(profileData.profile); + }; + div.appendChild(defaultButton); + } + + if (!profileData.isInUse) { + let runButton = document.createElement("button"); + document.l10n.setAttributes(runButton, "profiles-launch-profile"); + runButton.onclick = function () { + openProfile(profileData.profile); + }; + div.appendChild(runButton); + } + + let sep = document.createElement("hr"); + div.appendChild(sep); +} + +// This is called from the createProfileWizard.xhtml dialog. +function CreateProfile(profile) { + // The wizard created a profile, just make it the default. + defaultProfile(profile); +} + +function createProfileWizard() { + // This should be rewritten in HTML eventually. + window.browsingContext.topChromeWindow.openDialog( + "chrome://mozapps/content/profile/createProfileWizard.xhtml", + "", + "centerscreen,chrome,modal,titlebar", + ProfileService, + { CreateProfile } + ); +} + +async function renameProfile(profile) { + let newName = { value: profile.name }; + let [title, msg] = await document.l10n.formatValues([ + { id: "profiles-rename-profile-title" }, + { id: "profiles-rename-profile", args: { name: profile.name } }, + ]); + + if (Services.prompt.prompt(window, title, msg, newName, null, { value: 0 })) { + newName = newName.value; + + if (newName == profile.name) { + return; + } + + try { + profile.name = newName; + } catch (e) { + let [title, msg] = await document.l10n.formatValues([ + { id: "profiles-invalid-profile-name-title" }, + { id: "profiles-invalid-profile-name", args: { name: newName } }, + ]); + + Services.prompt.alert(window, title, msg); + return; + } + + flush(); + } +} + +async function removeProfile(profile) { + let deleteFiles = false; + + if (profile.rootDir.exists()) { + let [title, msg, dontDeleteStr, deleteStr] = + await document.l10n.formatValues([ + { id: "profiles-delete-profile-title" }, + { + id: "profiles-delete-profile-confirm", + args: { dir: profile.rootDir.path }, + }, + { id: "profiles-dont-delete-files" }, + { id: "profiles-delete-files" }, + ]); + let buttonPressed = Services.prompt.confirmEx( + window, + title, + msg, + Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 + + Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1 + + Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2, + dontDeleteStr, + null, + deleteStr, + null, + { value: 0 } + ); + if (buttonPressed == 1) { + return; + } + + if (buttonPressed == 2) { + deleteFiles = true; + } + } + + // If we are deleting the default profile we must choose a different one. + let isDefault = false; + try { + isDefault = ProfileService.defaultProfile == profile; + } catch (e) {} + + if (isDefault) { + for (let p of ProfileService.profiles) { + if (profile == p) { + continue; + } + + if (isDefault) { + try { + ProfileService.defaultProfile = p; + } catch (e) { + // This can happen on dev-edition if a non-default profile is in use. + // In such a case the next time that dev-edition is started it will + // find no default profile and just create a new one. + } + } + + break; + } + } + + try { + profile.removeInBackground(deleteFiles); + } catch (e) { + let [title, msg] = await document.l10n.formatValues([ + { id: "profiles-delete-profile-failed-title" }, + { id: "profiles-delete-profile-failed-message" }, + ]); + + Services.prompt.alert(window, title, msg); + return; + } + + flush(); +} + +async function defaultProfile(profile) { + try { + ProfileService.defaultProfile = profile; + flush(); + } catch (e) { + // This can happen on dev-edition. + let [title, msg] = await document.l10n.formatValues([ + { id: "profiles-cannot-set-as-default-title" }, + { id: "profiles-cannot-set-as-default-message" }, + ]); + + Services.prompt.alert(window, title, msg); + } +} + +function openProfile(profile) { + Services.startup.createInstanceWithProfile(profile); +} + +function restart(safeMode) { + let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance( + Ci.nsISupportsPRBool + ); + Services.obs.notifyObservers( + cancelQuit, + "quit-application-requested", + "restart" + ); + + if (cancelQuit.data) { + return; + } + + let flags = Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart; + + if (safeMode) { + Services.startup.restartInSafeMode(flags); + } else { + Services.startup.quit(flags); + } +} + +window.addEventListener( + "DOMContentLoaded", + function () { + let createButton = document.getElementById("create-button"); + createButton.addEventListener("click", createProfileWizard); + + let restartSafeModeButton = document.getElementById( + "restart-in-safe-mode-button" + ); + if (!Services.policies || Services.policies.isAllowed("safeMode")) { + restartSafeModeButton.addEventListener("click", () => { + restart(true); + }); + } else { + restartSafeModeButton.setAttribute("disabled", "true"); + } + + let restartNormalModeButton = document.getElementById("restart-button"); + restartNormalModeButton.addEventListener("click", () => { + restart(false); + }); + + if (ProfileService.isListOutdated) { + document.getElementById("owned").hidden = true; + } else { + document.getElementById("conflict").hidden = true; + rebuildProfileList(); + } + }, + { once: true } +); |