sidebar-page.mjs (6292B)
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 const lazy = {}; 6 7 import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; 8 import { html } from "chrome://global/content/vendor/lit.all.mjs"; 9 // eslint-disable-next-line mozilla/reject-import-system-module-from-non-system 10 import { PlacesUtils } from "resource://gre/modules/PlacesUtils.sys.mjs"; 11 // eslint-disable-next-line import/no-unassigned-import 12 import "chrome://browser/content/sidebar/sidebar-panel-header.mjs"; 13 14 ChromeUtils.defineESModuleGetters(lazy, { 15 BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", 16 PlacesUIUtils: "moz-src:///browser/components/places/PlacesUIUtils.sys.mjs", 17 }); 18 19 export class SidebarPage extends MozLitElement { 20 constructor() { 21 super(); 22 this.clearDocument = this.clearDocument.bind(this); 23 } 24 25 connectedCallback() { 26 super.connectedCallback(); 27 this.ownerGlobal.addEventListener("beforeunload", this.clearDocument); 28 this.ownerGlobal.addEventListener("unload", this.clearDocument); 29 30 this._contextMenu = this.topWindow.SidebarController.currentContextMenu; 31 } 32 33 disconnectedCallback() { 34 super.disconnectedCallback(); 35 this.ownerGlobal.removeEventListener("beforeunload", this.clearDocument); 36 this.ownerGlobal.removeEventListener("unload", this.clearDocument); 37 } 38 39 get topWindow() { 40 return this.ownerGlobal.top; 41 } 42 43 get sidebarController() { 44 return this.topWindow.SidebarController; 45 } 46 47 addContextMenuListeners() { 48 this.addEventListener("contextmenu", this); 49 this._contextMenu.addEventListener("command", this); 50 this._contextMenu.addEventListener( 51 "popupshowing", 52 this.placesContextShowing 53 ); 54 this._contextMenu.addEventListener("popuphiding", this.placesContextHiding); 55 } 56 57 removeContextMenuListeners() { 58 this.removeEventListener("contextmenu", this); 59 this._contextMenu.removeEventListener("command", this); 60 this._contextMenu.removeEventListener( 61 "popupshowing", 62 this.placesContextShowing 63 ); 64 this._contextMenu.removeEventListener( 65 "popuphiding", 66 this.placesContextHiding 67 ); 68 } 69 70 addSidebarFocusedListeners() { 71 this.topWindow.addEventListener("SidebarFocused", this); 72 } 73 74 removeSidebarFocusedListeners() { 75 this.topWindow.removeEventListener("SidebarFocused", this); 76 } 77 78 handleEvent(e) { 79 switch (e.type) { 80 case "contextmenu": 81 this.handleContextMenuEvent?.(e); 82 break; 83 case "command": 84 this.handleCommandEvent?.(e); 85 break; 86 case "SidebarFocused": 87 this.handleSidebarFocusedEvent?.(e); 88 break; 89 } 90 } 91 92 placesContextShowing(e) { 93 lazy.PlacesUIUtils.placesContextShowing(e); 94 } 95 96 placesContextHiding(e) { 97 lazy.PlacesUIUtils.placesContextHiding(e); 98 } 99 100 /** 101 * Check if this event comes from an element of the specified type. If it 102 * does, return that element. 103 * 104 * @param {Event} e 105 * The event to check. 106 * @param {string} localName 107 * The name of the element to match. 108 * @returns {Element | null} 109 * The matching element, or `null` if no match is found. 110 */ 111 findTriggerNode(e, localName) { 112 let elements = [ 113 e.explicitOriginalTarget, 114 e.originalTarget.flattenedTreeParentNode, 115 // Event might be in shadow DOM, check the host element. 116 e.explicitOriginalTarget.flattenedTreeParentNode.getRootNode().host, 117 e.originalTarget.flattenedTreeParentNode.getRootNode().host, 118 ]; 119 for (let el of elements) { 120 if (el?.localName == localName) { 121 return el; 122 } 123 } 124 return null; 125 } 126 127 /** 128 * Handle a command if it is a common one that is used in multiple pages. 129 * Commands specific to a page should be handled in a subclass. 130 * 131 * @param {Event} e 132 * The event to handle. 133 */ 134 handleCommandEvent(e) { 135 switch (e.target.id) { 136 case "sidebar-history-context-open-in-tab": 137 this.topWindow.openTrustedLinkIn(this.triggerNode.url, "tab"); 138 break; 139 case "sidebar-history-context-forget-site": 140 this.forgetAboutThisSite().catch(console.error); 141 break; 142 case "sidebar-history-context-open-in-window": 143 case "sidebar-synced-tabs-context-open-in-window": 144 this.topWindow.openTrustedLinkIn(this.triggerNode.url, "window", { 145 private: false, 146 }); 147 break; 148 case "sidebar-history-context-open-in-private-window": 149 case "sidebar-synced-tabs-context-open-in-private-window": 150 this.topWindow.openTrustedLinkIn(this.triggerNode.url, "window", { 151 private: true, 152 }); 153 break; 154 case "sidebar-history-context-copy-link": 155 case "sidebar-synced-tabs-context-copy-link": 156 lazy.BrowserUtils.copyLink( 157 this.triggerNode.url, 158 this.triggerNode.title 159 ); 160 break; 161 case "sidebar-synced-tabs-context-bookmark-tab": 162 case "sidebar-history-context-bookmark-page": 163 this.topWindow.PlacesCommandHook.bookmarkLink( 164 this.triggerNode.url, 165 this.triggerNode.title 166 ); 167 break; 168 } 169 } 170 171 async forgetAboutThisSite() { 172 let host; 173 if (PlacesUtils.nodeIsHost(this.triggerNode)) { 174 host = this.triggerNode.query.domain; 175 } else { 176 host = Services.io.newURI(this.triggerNode.url).host; 177 } 178 let baseDomain; 179 try { 180 baseDomain = Services.eTLD.getBaseDomainFromHost(host); 181 } catch (e) { 182 // If there is no baseDomain we fall back to host 183 } 184 await this.topWindow.gDialogBox.open( 185 "chrome://browser/content/places/clearDataForSite.xhtml", 186 { host, hostOrBaseDomain: baseDomain ?? host } 187 ); 188 } 189 190 /** 191 * Clear out the document so the disconnectedCallback() will trigger properly 192 * and all of the custom elements can cleanup. 193 */ 194 clearDocument() { 195 this.ownerGlobal.document.body.textContent = ""; 196 } 197 198 /** 199 * The common stylesheet for all sidebar pages. 200 * 201 * @returns {TemplateResult} 202 */ 203 stylesheet() { 204 return html` 205 <link 206 rel="stylesheet" 207 href="chrome://browser/content/sidebar/sidebar.css" 208 /> 209 `; 210 } 211 }