firefoxview.mjs (5327B)
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 let pageList = []; 6 let viewsDeck = null; 7 let pageNav = null; 8 let activeComponent = null; 9 let searchKeyboardShortcut = null; 10 11 const { topChromeWindow } = window.browsingContext; 12 13 function onHashChange() { 14 let view = document.location?.hash.substring(1); 15 if (!view || !pageList.includes(view)) { 16 view = "recentbrowsing"; 17 } 18 changeView(view); 19 } 20 21 function changeView(view) { 22 viewsDeck.selectedViewName = view; 23 pageNav.currentView = view; 24 } 25 26 function onViewsDeckViewChange() { 27 for (const child of viewsDeck.children) { 28 if (child.getAttribute("name") == viewsDeck.selectedViewName) { 29 child.enter(); 30 activeComponent = child; 31 } else { 32 child.exit(); 33 } 34 } 35 } 36 37 function recordNavigationTelemetry(source, eventTarget) { 38 let view = "recentbrowsing"; 39 if (source === "category-navigation") { 40 view = eventTarget.parentNode.currentView; 41 } else if (source === "view-all") { 42 view = eventTarget.shortPageName; 43 } 44 // Record telemetry 45 Glean.firefoxviewNext.changePageNavigation.record({ 46 page: view, 47 source, 48 }); 49 } 50 51 async function updateSearchTextboxSize() { 52 const msgs = [ 53 { id: "firefoxview-search-text-box-recentbrowsing" }, 54 { id: "firefoxview-search-text-box-opentabs" }, 55 { id: "firefoxview-search-text-box-recentlyclosed" }, 56 { id: "firefoxview-search-text-box-tabs" }, 57 { id: "firefoxview-search-text-box-history" }, 58 ]; 59 let maxLength = 30; 60 for (const msg of await document.l10n.formatMessages(msgs)) { 61 const placeholder = msg.attributes[0].value; 62 maxLength = Math.max(maxLength, placeholder.length); 63 } 64 for (const child of viewsDeck.children) { 65 child.searchTextboxSize = maxLength; 66 } 67 } 68 69 async function updateSearchKeyboardShortcut() { 70 const [message] = await topChromeWindow.document.l10n.formatMessages([ 71 { id: "find-shortcut" }, 72 ]); 73 const key = message.attributes[0].value; 74 searchKeyboardShortcut = key.toLocaleLowerCase(); 75 } 76 77 function updateSyncVisibility() { 78 const syncEnabled = Services.prefs.getBoolPref( 79 "identity.fxaccounts.enabled", 80 false 81 ); 82 for (const el of document.querySelectorAll(".sync-ui-item")) { 83 el.hidden = !syncEnabled; 84 } 85 } 86 87 window.addEventListener("DOMContentLoaded", async () => { 88 recordEnteredTelemetry(); 89 90 pageNav = document.querySelector("moz-page-nav"); 91 viewsDeck = document.querySelector("named-deck"); 92 93 for (const item of pageNav.pageNavButtons) { 94 pageList.push(item.getAttribute("view")); 95 } 96 window.addEventListener("hashchange", onHashChange); 97 window.addEventListener("change-view", function (event) { 98 location.hash = event.target.getAttribute("view"); 99 window.scrollTo(0, 0); 100 recordNavigationTelemetry("category-navigation", event.target); 101 }); 102 window.addEventListener("card-container-view-all", function (event) { 103 recordNavigationTelemetry("view-all", event.originalTarget); 104 }); 105 106 viewsDeck.addEventListener("view-changed", onViewsDeckViewChange); 107 108 // set the initial state 109 onHashChange(); 110 onViewsDeckViewChange(); 111 await updateSearchTextboxSize(); 112 await updateSearchKeyboardShortcut(); 113 updateSyncVisibility(); 114 115 if (Cu.isInAutomation) { 116 Services.obs.notifyObservers(null, "firefoxview-entered"); 117 } 118 }); 119 120 document.addEventListener("visibilitychange", () => { 121 if (document.visibilityState === "visible") { 122 recordEnteredTelemetry(); 123 if (Cu.isInAutomation) { 124 // allow all the component visibilitychange handlers to execute before notifying 125 requestAnimationFrame(() => { 126 Services.obs.notifyObservers(null, "firefoxview-entered"); 127 }); 128 } 129 } 130 }); 131 132 function recordEnteredTelemetry() { 133 Glean.firefoxviewNext.enteredFirefoxview.record({ 134 page: document.location?.hash?.substring(1) || "recentbrowsing", 135 }); 136 } 137 138 document.addEventListener("keydown", e => { 139 if (e.getModifierState("Accel") && e.key === searchKeyboardShortcut) { 140 activeComponent.searchTextbox?.focus(); 141 } 142 }); 143 144 window.addEventListener( 145 "unload", 146 () => { 147 // Clear out the document so the disconnectedCallback will trigger 148 // properly and all of the custom elements can cleanup. 149 document.body.textContent = ""; 150 topChromeWindow.removeEventListener("command", onCommand); 151 Services.obs.removeObserver(onLocalesChanged, "intl:app-locales-changed"); 152 Services.prefs.removeObserver( 153 "identity.fxaccounts.enabled", 154 updateSyncVisibility 155 ); 156 }, 157 { once: true } 158 ); 159 160 topChromeWindow.addEventListener("command", onCommand); 161 Services.obs.addObserver(onLocalesChanged, "intl:app-locales-changed"); 162 Services.prefs.addObserver("identity.fxaccounts.enabled", updateSyncVisibility); 163 164 function onCommand(e) { 165 if (document.hidden || !e.target.closest("#contentAreaContextMenu")) { 166 return; 167 } 168 const item = 169 e.target.closest("#context-openlinkinusercontext-menu") || e.target; 170 Glean.firefoxviewNext.browserContextMenuTabs.record({ 171 menu_action: item.id, 172 page: location.hash?.substring(1) || "recentbrowsing", 173 }); 174 } 175 176 function onLocalesChanged() { 177 requestIdleCallback(() => { 178 updateSearchTextboxSize(); 179 updateSearchKeyboardShortcut(); 180 }); 181 }