profile-selector.mjs (5814B)
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 import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; 6 import { html } from "chrome://global/content/vendor/lit.all.mjs"; 7 8 // eslint-disable-next-line import/no-unassigned-import 9 import "chrome://browser/content/profiles/profile-card.mjs"; 10 // eslint-disable-next-line import/no-unassigned-import 11 import "chrome://global/content/elements/moz-checkbox.mjs"; 12 13 const { SelectableProfileService } = ChromeUtils.importESModule( 14 "resource:///modules/profiles/SelectableProfileService.sys.mjs" 15 ); 16 17 /** 18 * The element for display SelectableProfiles in the profile selector window 19 */ 20 export class ProfileSelector extends MozLitElement { 21 static properties = { 22 profiles: { type: Array }, 23 showSelector: { type: Boolean }, 24 }; 25 26 static queries = { 27 checkbox: "moz-checkbox", 28 profileCards: { all: "profile-card" }, 29 createProfileCard: "new-profile-card", 30 }; 31 32 #initPromise = null; 33 #startupParams = null; 34 35 constructor() { 36 super(); 37 38 this.#initPromise = this.init(); 39 if (window.arguments?.[0] instanceof Ci.nsIDialogParamBlock) { 40 this.#startupParams = window.arguments[0]; 41 } 42 } 43 44 get isStartupUI() { 45 return !!this.#startupParams; 46 } 47 48 /** 49 * Sets the return block for the startup UI. 50 * 51 * @param {SelectableProfile} profile The profile to launch 52 * @param {string[]} args Any additional command line arguments to append 53 */ 54 async setLaunchArguments(profile, args = []) { 55 if (!this.#startupParams) { 56 return; 57 } 58 59 this.#startupParams.SetInt( 60 0, 61 Ci.nsIToolkitProfileService.launchWithProfile 62 ); 63 // Set start offline to false. 64 this.#startupParams.SetInt(1, 0); 65 // Number of new arguments. 66 this.#startupParams.SetInt(2, args.length); 67 68 this.#startupParams.objects.insertElementAt(await profile.rootDir, 0); 69 this.#startupParams.objects.insertElementAt(await profile.localDir, 1); 70 71 this.#startupParams.SetNumberStrings(args.length); 72 for (let i = 0; i < args.length; i++) { 73 this.#startupParams.SetString(i, args[i]); 74 } 75 } 76 77 async getUpdateComplete() { 78 let result = await super.getUpdateComplete(); 79 await this.#initPromise; 80 return result; 81 } 82 83 async init() { 84 if (this.initialized) { 85 return; 86 } 87 88 document.addEventListener("LaunchProfile", this); 89 document.addEventListener("CreateProfile", this); 90 document.addEventListener("DeleteProfile", this); 91 92 this.selectableProfileService = SelectableProfileService; 93 94 await this.selectableProfileService.init(); 95 await this.selectableProfileService.maybeSetupDataStore(); 96 this.profiles = await this.selectableProfileService.getAllProfiles(); 97 this.showSelector = 98 this.selectableProfileService.groupToolkitProfile.showProfileSelector; 99 100 if (!this.profiles.length) { 101 await this.selectableProfileService.setShowProfileSelectorWindow(false); 102 } 103 104 this.initialized = true; 105 this.#initPromise = null; 106 107 if (this.isStartupUI) { 108 window.addEventListener("unload", () => { 109 // In case the user closed the window manually. 110 this.selectableProfileService.uninit(); 111 }); 112 } 113 } 114 115 handleCheckboxToggle() { 116 this.showSelector = this.checkbox.checked; 117 let state = this.showSelector ? "enabled" : "disabled"; 118 Glean.profilesSelectorWindow.showAtStartup.record({ value: state }); 119 this.selectableProfileService.setShowProfileSelectorWindow( 120 this.showSelector 121 ); 122 } 123 124 async launchProfile(profile, url) { 125 if (this.isStartupUI) { 126 await this.setLaunchArguments(profile, url ? ["-url", url] : []); 127 await this.selectableProfileService.uninit(); 128 } else { 129 this.selectableProfileService.launchInstance(profile, url ? [url] : []); 130 } 131 132 window.close(); 133 } 134 135 async handleEvent(event) { 136 switch (event.type) { 137 case "LaunchProfile": { 138 Glean.profilesSelectorWindow.launch.record(); 139 let { profile, url } = event.detail; 140 await this.launchProfile(profile, url); 141 break; 142 } 143 case "CreateProfile": { 144 let profile = 145 await this.selectableProfileService.createNewProfile(false); 146 await this.launchProfile(profile, "about:newprofile"); 147 break; 148 } 149 case "DeleteProfile": { 150 let profile = event.detail; 151 await this.launchProfile(profile, "about:deleteprofile"); 152 break; 153 } 154 } 155 } 156 157 render() { 158 if (!this.profiles) { 159 return null; 160 } 161 162 return html`<link 163 rel="stylesheet" 164 href="chrome://browser/content/profiles/profile-selector.css" 165 /> 166 <link 167 rel="stylesheet" 168 href="chrome://global/skin/in-content/common.css" 169 /> 170 <img 171 class="logo" 172 data-l10n-id="profile-window-logo" 173 data-l10n-attrs="alt" 174 src="chrome://branding/content/about-logo.svg" 175 /> 176 <h1 data-l10n-id="profile-window-heading"></h1> 177 <p class="profiles-body-text" data-l10n-id="profile-window-body"></p> 178 <div class="profile-list"> 179 ${this.profiles.map( 180 p => html`<profile-card .profile=${p}></profile-card>` 181 )} 182 <new-profile-card></new-profile-card> 183 </div> 184 <moz-checkbox 185 @click=${this.handleCheckboxToggle} 186 data-l10n-id="profile-window-checkbox-label-2" 187 ?checked=${this.showSelector} 188 > 189 <span 190 slot="description" 191 data-l10n-id="profile-window-checkbox-subcopy" 192 ?hidden=${this.showSelector} 193 ></span> 194 </moz-checkbox>`; 195 } 196 } 197 198 customElements.define("profile-selector", ProfileSelector);