fxrui.js (8672B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* import-globals-from common.js */ 7 /* import-globals-from permissions.js */ 8 9 // Configuration vars 10 let homeURL = "https://webxr.today/"; 11 // Bug 1586294 - Localize the privacy policy URL (Services.urlFormatter?) 12 let privacyPolicyURL = "https://www.mozilla.org/en-US/privacy/firefox/"; 13 let reportIssueURL = "https://mzl.la/fxr"; 14 let licenseURL = 15 "https://mixedreality.mozilla.org/FirefoxRealityPC/license.html"; 16 17 // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/browser 18 let browser = null; 19 // Keep track of the current Permissions request to only allow one outstanding 20 // request/prompt at a time. 21 let currentPermissionRequest = null; 22 // And, keep a queue of pending Permissions requests to resolve when the 23 // current request finishes 24 let pendingPermissionRequests = []; 25 // The following variable map to UI elements whose behavior changes depending 26 // on some state from the browser control 27 let urlInput = null; 28 let secureIcon = null; 29 let backButton = null; 30 let forwardButton = null; 31 let refreshButton = null; 32 let stopButton = null; 33 34 const { PrivateBrowsingUtils } = ChromeUtils.importESModule( 35 "resource://gre/modules/PrivateBrowsingUtils.sys.mjs" 36 ); 37 const { AppConstants } = ChromeUtils.importESModule( 38 "resource://gre/modules/AppConstants.sys.mjs" 39 ); 40 const { XPCOMUtils } = ChromeUtils.importESModule( 41 "resource://gre/modules/XPCOMUtils.sys.mjs" 42 ); 43 44 // Note: FxR UI uses a fork of browser-fullScreenAndPointerLock.js which removes 45 // the dependencies on browser.js. 46 // Bug 1587946 - Rationalize the fork of browser-fullScreenAndPointerLock.js 47 XPCOMUtils.defineLazyScriptGetter( 48 this, 49 "FullScreen", 50 "chrome://fxr/content/fxr-fullScreen.js" 51 ); 52 ChromeUtils.defineLazyGetter(this, "gSystemPrincipal", () => 53 Services.scriptSecurityManager.getSystemPrincipal() 54 ); 55 56 window.addEventListener( 57 "DOMContentLoaded", 58 () => { 59 urlInput = document.getElementById("eUrlInput"); 60 secureIcon = document.getElementById("eUrlSecure"); 61 backButton = document.getElementById("eBack"); 62 forwardButton = document.getElementById("eForward"); 63 refreshButton = document.getElementById("eRefresh"); 64 stopButton = document.getElementById("eStop"); 65 66 setupBrowser(); 67 setupNavButtons(); 68 setupUrlBar(); 69 }, 70 { once: true } 71 ); 72 73 // Create XUL browser object 74 function setupBrowser() { 75 // Note: createXULElement is undefined when this page is not loaded 76 // via chrome protocol 77 if (document.createXULElement) { 78 browser = document.createXULElement("browser"); 79 browser.setAttribute("type", "content"); 80 browser.setAttribute("remote", "true"); 81 browser.classList.add("browser_instance"); 82 document.getElementById("eBrowserContainer").appendChild(browser); 83 84 browser.loadUrlWithSystemPrincipal = function (url) { 85 this.loadURI(url, { triggeringPrincipal: gSystemPrincipal }); 86 }; 87 88 // Expose this function for Permissions to be used on this browser element 89 // in other parts of the frontend 90 browser.fxrPermissionPrompt = permissionPrompt; 91 92 urlInput.value = homeURL; 93 browser.loadUrlWithSystemPrincipal(homeURL); 94 95 browser.addProgressListener( 96 { 97 QueryInterface: ChromeUtils.generateQI([ 98 "nsIWebProgressListener", 99 "nsISupportsWeakReference", 100 ]), 101 onLocationChange() { 102 // When URL changes, update the URL in the URL bar and update 103 // whether the back/forward buttons are enabled. 104 urlInput.value = browser.currentURI.spec; 105 106 backButton.disabled = !browser.canGoBack; 107 forwardButton.disabled = !browser.canGoForward; 108 }, 109 onStateChange(aWebProgress, aRequest, aStateFlags) { 110 if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) { 111 // Network requests are complete. Disable (hide) the stop button 112 // and enable (show) the refresh button 113 refreshButton.disabled = false; 114 stopButton.disabled = true; 115 } else { 116 // Network requests are outstanding. Disable (hide) the refresh 117 // button and enable (show) the stop button 118 refreshButton.disabled = true; 119 stopButton.disabled = false; 120 } 121 }, 122 onSecurityChange(aWebProgress, aRequest, aState) { 123 // Update the Secure Icon when the security status of the 124 // content changes 125 if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) { 126 secureIcon.style.visibility = "visible"; 127 } else { 128 secureIcon.style.visibility = "hidden"; 129 } 130 }, 131 }, 132 Ci.nsIWebProgress.NOTIFY_LOCATION | 133 Ci.nsIWebProgress.NOTIFY_SECURITY | 134 Ci.nsIWebProgress.NOTIFY_STATE_REQUEST 135 ); 136 137 FullScreen.init(); 138 139 // Send this notification to start and allow background scripts for 140 // WebExtensions, since this FxR UI doesn't participate in typical 141 // startup activities 142 Services.obs.notifyObservers(window, "extensions-late-startup"); 143 } 144 } 145 146 function setupNavButtons() { 147 let aryNavButtons = [ 148 "eBack", 149 "eForward", 150 "eRefresh", 151 "eStop", 152 "eHome", 153 "ePrefs", 154 ]; 155 156 function navButtonHandler() { 157 if (!this.disabled) { 158 switch (this.id) { 159 case "eBack": 160 browser.goBack(); 161 break; 162 163 case "eForward": 164 browser.goForward(); 165 break; 166 167 case "eRefresh": 168 browser.reload(); 169 break; 170 171 case "eStop": 172 browser.stop(); 173 break; 174 175 case "eHome": 176 browser.loadUrlWithSystemPrincipal(homeURL); 177 break; 178 179 case "ePrefs": 180 openSettings(); 181 break; 182 } 183 } 184 } 185 186 for (let btnName of aryNavButtons) { 187 let elem = document.getElementById(btnName); 188 elem.addEventListener("click", navButtonHandler); 189 } 190 } 191 192 function setupUrlBar() { 193 // Navigate to new value when the user presses "Enter" 194 urlInput.addEventListener("keypress", async function (e) { 195 if (e.key == "Enter") { 196 // Use the URL Fixup Service in case the user wants to search instead 197 // of directly navigating to a location. 198 await Services.search.init(); 199 200 let valueToFixUp = urlInput.value; 201 let flags = 202 Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | 203 Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; 204 if (PrivateBrowsingUtils.isWindowPrivate(window)) { 205 flags |= Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT; 206 } 207 let { preferredURI } = Services.uriFixup.getFixupURIInfo( 208 valueToFixUp, 209 flags 210 ); 211 212 browser.loadUrlWithSystemPrincipal(preferredURI.spec); 213 browser.focus(); 214 } 215 }); 216 217 // Upon focus, highlight the whole URL 218 urlInput.addEventListener("focus", function () { 219 urlInput.select(); 220 }); 221 } 222 223 // 224 // Code to manage Settings UI 225 // 226 227 function openSettings() { 228 let browserSettingsUI = document.createXULElement("browser"); 229 browserSettingsUI.setAttribute("type", "chrome"); 230 browserSettingsUI.classList.add("browser_settings"); 231 232 showModalContainer(browserSettingsUI); 233 234 browserSettingsUI.loadURI("chrome://fxr/content/prefs.html", { 235 triggeringPrincipal: gSystemPrincipal, 236 }); 237 } 238 239 function closeSettings() { 240 clearModalContainer(); 241 } 242 243 function showPrivacyPolicy() { 244 closeSettings(); 245 browser.loadUrlWithSystemPrincipal(privacyPolicyURL); 246 } 247 248 function showLicenseInfo() { 249 closeSettings(); 250 browser.loadUrlWithSystemPrincipal(licenseURL); 251 } 252 253 function showReportIssue() { 254 closeSettings(); 255 browser.loadUrlWithSystemPrincipal(reportIssueURL); 256 } 257 258 // 259 // Code to manage Permissions UI 260 // 261 262 function permissionPrompt(aRequest) { 263 let newPrompt; 264 if (aRequest instanceof Ci.nsIContentPermissionRequest) { 265 newPrompt = new FxrContentPrompt(aRequest, this, finishPrompt); 266 } else { 267 newPrompt = new FxrWebRTCPrompt(aRequest, this, finishPrompt); 268 } 269 270 if (currentPermissionRequest) { 271 // There is already an outstanding request running. Cache this new request 272 // to be prompted later 273 pendingPermissionRequests.push(newPrompt); 274 } else { 275 currentPermissionRequest = newPrompt; 276 currentPermissionRequest.showPrompt(); 277 } 278 } 279 280 function finishPrompt() { 281 if (pendingPermissionRequests.length) { 282 // Prompt the next request 283 currentPermissionRequest = pendingPermissionRequests.shift(); 284 currentPermissionRequest.showPrompt(); 285 } else { 286 currentPermissionRequest = null; 287 } 288 }