moreFromMozilla.js (9460B)
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 file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* import-globals-from preferences.js */ 6 7 var gMoreFromMozillaPane = { 8 initialized: false, 9 10 /** 11 * "default" is whatever template is the default, as defined by the code 12 * in this file (currently in `getTemplateName`). Setting option to an 13 * invalid value will leave it unchanged. 14 */ 15 _option: "default", 16 set option(value) { 17 if (!value) { 18 this._option = "default"; 19 return; 20 } 21 22 if (value === "default" || value === "simple") { 23 this._option = value; 24 } 25 }, 26 27 get option() { 28 return this._option; 29 }, 30 31 getTemplateName() { 32 if (!this._option || this._option == "default") { 33 return "simple"; 34 } 35 return this._option; 36 }, 37 38 getURL(url, region, option, hasEmail) { 39 const URL_PARAMS = { 40 utm_source: "about-prefs", 41 utm_campaign: "morefrommozilla", 42 utm_medium: "firefox-desktop", 43 }; 44 // UTM content param used in analytics to record 45 // UI template used to open URL 46 const utm_content = { 47 default: "default", 48 simple: "fxvt-113-a", 49 }; 50 51 const experiment_params = { 52 entrypoint_experiment: "morefrommozilla-experiment-1846", 53 }; 54 55 let pageUrl = new URL(url); 56 for (let [key, val] of Object.entries(URL_PARAMS)) { 57 pageUrl.searchParams.append(key, val); 58 } 59 60 // Append region by product to utm_content and also 61 // append '-email' when URL is opened 62 // from send email link in QRCode box 63 if (option) { 64 pageUrl.searchParams.set( 65 "utm_content", 66 `${utm_content[option]}-${region}${hasEmail ? "-email" : ""}` 67 ); 68 } 69 70 // Add experiments params when user is shown an experimental UI 71 // with template value as 'simple' set via Nimbus 72 if (option !== "default") { 73 pageUrl.searchParams.set( 74 "entrypoint_experiment", 75 experiment_params.entrypoint_experiment 76 ); 77 pageUrl.searchParams.set("entrypoint_variation", `treatment-${option}`); 78 } 79 return pageUrl.toString(); 80 }, 81 82 renderProducts() { 83 const isRegionUS = Region.home.toLowerCase() === "us"; 84 let products = [ 85 { 86 id: "firefox-mobile", 87 title_string_id: "more-from-moz-firefox-mobile-title", 88 description_string_id: "more-from-moz-firefox-mobile-description", 89 region: "global", 90 button: { 91 id: "fxMobile", 92 type: "link", 93 label_string_id: "more-from-moz-learn-more-link", 94 actionURL: BrowserUtils.isChinaRepack() 95 ? "https://www.firefox.com.cn/browsers/mobile/" 96 : "https://www.mozilla.org/firefox/browsers/mobile/", 97 }, 98 qrcode: { 99 title: { 100 string_id: "more-from-moz-qr-code-box-firefox-mobile-title", 101 }, 102 image_src_prefix: 103 "chrome://browser/content/preferences/more-from-mozilla-qr-code", 104 button: { 105 id: "qr-code-send-email", 106 label: { 107 string_id: "more-from-moz-qr-code-box-firefox-mobile-button", 108 }, 109 actionURL: BrowserUtils.isChinaRepack() 110 ? "https://www.firefox.com.cn/mobile/get-app/" 111 : "https://www.mozilla.org/firefox/mobile/get-app/?v=mfm", 112 }, 113 }, 114 }, 115 { 116 id: "mozilla-monitor", 117 title_string_id: "more-from-moz-mozilla-monitor-title", 118 description_string_id: 119 "more-from-moz-mozilla-monitor-global-description", 120 region: isRegionUS ? "us" : "global", 121 button: { 122 id: "mozillaMonitor", 123 label_string_id: "more-from-moz-mozilla-monitor-button", 124 actionURL: "https://monitor.mozilla.org/", 125 }, 126 }, 127 ]; 128 129 if (BrowserUtils.shouldShowVPNPromo()) { 130 const vpn = { 131 id: "mozilla-vpn", 132 title_string_id: "more-from-moz-mozilla-vpn-title", 133 description_string_id: "more-from-moz-mozilla-vpn-description", 134 region: "global", 135 button: { 136 id: "mozillaVPN", 137 label_string_id: "more-from-moz-button-mozilla-vpn-2", 138 actionURL: "https://www.mozilla.org/products/vpn/", 139 }, 140 }; 141 products.push(vpn); 142 } 143 144 if (BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY)) { 145 const relay = { 146 id: "firefox-relay", 147 title_string_id: "more-from-moz-firefox-relay-title", 148 description_string_id: "more-from-moz-firefox-relay-description", 149 region: "global", 150 button: { 151 id: "firefoxRelay", 152 label_string_id: "more-from-moz-firefox-relay-button", 153 actionURL: "https://relay.firefox.com/", 154 }, 155 }; 156 products.push(relay); 157 } 158 159 products.push({ 160 id: "solo-ai", 161 title_string_id: "more-from-moz-solo-title-2", 162 description_string_id: "more-from-moz-solo-description", 163 region: "global", 164 button: { 165 id: "soloAI", 166 label_string_id: "more-from-moz-solo-button", 167 actionURL: "https://soloist.ai/?utm_type=more_from_mozilla", 168 }, 169 }); 170 171 products.push({ 172 id: "mdn", 173 title_string_id: "more-from-moz-mdn-title", 174 description_string_id: "more-from-moz-mdn-description", 175 region: "global", 176 button: { 177 id: "mdn", 178 label_string_id: "more-from-moz-mdn-button", 179 actionURL: "https://developer.mozilla.org/docs/Learn_web_development", 180 }, 181 }); 182 183 this._productsContainer = document.getElementById( 184 "moreFromMozillaCategory" 185 ); 186 let frag = document.createDocumentFragment(); 187 this._template = document.getElementById(this.getTemplateName()); 188 189 // Exit when internal data is incomplete 190 if (!this._template) { 191 return; 192 } 193 194 for (let product of products) { 195 let template = this._template.content.cloneNode(true); 196 let title = template.querySelector(".product-title"); 197 let desc = template.querySelector(".description"); 198 199 document.l10n.setAttributes(title, product.title_string_id); 200 title.id = product.id; 201 202 document.l10n.setAttributes(desc, product.description_string_id); 203 204 let isLink = product.button.type === "link"; 205 let actionElement = template.querySelector( 206 isLink ? ".text-link" : ".small-button" 207 ); 208 209 if (actionElement) { 210 actionElement.hidden = false; 211 actionElement.id = `${this.option}-${product.button.id}`; 212 document.l10n.setAttributes( 213 actionElement, 214 product.button.label_string_id 215 ); 216 217 if (isLink) { 218 actionElement.setAttribute( 219 "href", 220 this.getURL(product.button.actionURL, product.region, this.option) 221 ); 222 } else { 223 actionElement.addEventListener("click", function () { 224 let mainWindow = window.windowRoot.ownerGlobal; 225 mainWindow.openTrustedLinkIn( 226 gMoreFromMozillaPane.getURL( 227 product.button.actionURL, 228 product.region, 229 gMoreFromMozillaPane.option 230 ), 231 "tab" 232 ); 233 }); 234 } 235 } 236 237 if (product.qrcode) { 238 let qrcode = template.querySelector(".qr-code-box"); 239 qrcode.setAttribute("hidden", "false"); 240 241 let qrcode_title = template.querySelector(".qr-code-box-title"); 242 document.l10n.setAttributes( 243 qrcode_title, 244 product.qrcode.title.string_id 245 ); 246 247 let img = template.querySelector(".qr-code-box-image"); 248 // Append QRCode image source by template. For CN region 249 // simple template, we want a CN specific QRCode 250 img.src = 251 product.qrcode.image_src_prefix + 252 "-" + 253 this.getTemplateName() + 254 `${ 255 BrowserUtils.isChinaRepack() && 256 this.getTemplateName().includes("simple") 257 ? "-cn" 258 : "" 259 }` + 260 ".svg"; 261 // Add image a11y attributes 262 document.l10n.setAttributes( 263 img, 264 "more-from-moz-qr-code-firefox-mobile-img" 265 ); 266 267 let qrc_link = template.querySelector(".qr-code-link"); 268 269 // So the telemetry includes info about which option is being used 270 qrc_link.id = `${this.option}-${product.qrcode.button.id}`; 271 272 // For supported locales, this link allows users to send themselves a 273 // download link by email. It should be hidden for unsupported locales. 274 if (BrowserUtils.sendToDeviceEmailsSupported()) { 275 document.l10n.setAttributes( 276 qrc_link, 277 product.qrcode.button.label.string_id 278 ); 279 qrc_link.href = this.getURL( 280 product.qrcode.button.actionURL, 281 product.region, 282 this.option, 283 true 284 ); 285 qrc_link.hidden = false; 286 } 287 } 288 289 frag.appendChild(template); 290 } 291 this._productsContainer.appendChild(frag); 292 }, 293 294 async init() { 295 if (this.initialized) { 296 return; 297 } 298 this.initialized = true; 299 document 300 .getElementById("moreFromMozillaCategory") 301 .removeAttribute("data-hidden-from-search"); 302 document 303 .getElementById("moreFromMozillaCategory-header") 304 .removeAttribute("data-hidden-from-search"); 305 306 this.renderProducts(); 307 }, 308 };