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/. */
7 const EventEmitter = require("resource://devtools/shared/event-emitter.js");
9 function ToolSidebar(tabbox, panel, uid, options = {}) {
10 EventEmitter.decorate(this);
12 this._tabbox = tabbox;
14 this._panelDoc = this._tabbox.ownerDocument;
15 this._toolPanel = panel;
16 this._options = options;
18 if (!options.disableTelemetry) {
19 this._telemetry = this._toolPanel.telemetry;
24 if (this._options.hideTabstripe) {
25 this._tabbox.setAttribute("hidetabs", "true");
30 this._toolPanel.emit("sidebar-created", this);
33 exports.ToolSidebar = ToolSidebar;
35 ToolSidebar.prototype = {
36 TABPANEL_ID_PREFIX: "sidebar-panel-",
41 return this._toolPanel.React;
45 return this._toolPanel.ReactDOM;
48 get browserRequire() {
49 return this._toolPanel.browserRequire;
52 get InspectorTabPanel() {
53 return this._toolPanel.InspectorTabPanel;
57 return this._toolPanel.TabBar;
63 const sidebar = this.TabBar({
64 menuDocument: this._toolPanel._toolbox.doc,
65 showAllTabsMenu: true,
66 allTabsMenuButtonTooltip: this._options.allTabsMenuButtonTooltip,
67 sidebarToggleButton: this._options.sidebarToggleButton,
68 onSelect: this.handleSelectionChange.bind(this),
71 this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
75 * Adds all the queued tabs.
78 this._tabbar.addAllQueuedTabs();
82 * Register a side-panel tab.
84 * @param {String} tab uniq id
85 * @param {String} title tab title
86 * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
87 * @param {Boolean} selected true if the panel should be selected
88 * @param {Number} index the position where the tab should be inserted
90 addTab(id, title, panel, selected, index) {
91 this._tabbar.addTab(id, title, selected, panel, null, index);
92 this.emit("new-tab-registered", id);
96 * Helper API for adding side-panels that use existing DOM nodes
97 * (defined within inspector.xhtml) as the content.
99 * @param {String} tab uniq id
100 * @param {String} title tab title
101 * @param {Boolean} selected true if the panel should be selected
102 * @param {Number} index the position where the tab should be inserted
104 addExistingTab(id, title, selected, index) {
105 const panel = this.InspectorTabPanel({
107 idPrefix: this.TABPANEL_ID_PREFIX,
112 this.addTab(id, title, panel, selected, index);
116 * Queues a side-panel tab to be added..
118 * @param {String} tab uniq id
119 * @param {String} title tab title
120 * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
121 * @param {Boolean} selected true if the panel should be selected
122 * @param {Number} index the position where the tab should be inserted
124 queueTab(id, title, panel, selected, index) {
125 this._tabbar.queueTab(id, title, selected, panel, null, index);
126 this.emit("new-tab-registered", id);
130 * Helper API for queuing side-panels that use existing DOM nodes
131 * (defined within inspector.xhtml) as the content.
133 * @param {String} tab uniq id
134 * @param {String} title tab title
135 * @param {Boolean} selected true if the panel should be selected
136 * @param {Number} index the position where the tab should be inserted
138 queueExistingTab(id, title, selected, index) {
139 const panel = this.InspectorTabPanel({
141 idPrefix: this.TABPANEL_ID_PREFIX,
146 this.queueTab(id, title, panel, selected, index);
150 * Remove an existing tab.
151 * @param {String} tabId The ID of the tab that was used to register it, or
152 * the tab id attribute value if the tab existed before the sidebar
154 * @param {String} tabPanelId Optional. If provided, this ID will be used
155 * instead of the tabId to retrieve and remove the corresponding <tabpanel>
157 removeTab(tabId, tabPanelId) {
158 this._tabbar.removeTab(tabId);
160 this.emit("tab-unregistered", tabId);
164 * Show or hide a specific tab.
165 * @param {Boolean} isVisible True to show the tab/tabpanel, False to hide it.
166 * @param {String} id The ID of the tab to be hidden.
168 toggleTab(isVisible, id) {
169 this._tabbar.toggleTab(id, isVisible);
173 * Select a specific tab.
176 this._tabbar.select(id);
180 * Return the id of the selected tab.
183 return this._currentTool;
187 * Returns the requested tab panel based on the id.
192 // Search with and without the ID prefix as there might have been existing
193 // tabpanels by the time the sidebar got created
194 return this._panelDoc.querySelector(
195 "#" + this.TABPANEL_ID_PREFIX + id + ", #" + id
202 handleSelectionChange(id) {
203 if (this._destroyed) {
207 const previousTool = this._currentTool;
209 this.emit(previousTool + "-unselected");
212 this._currentTool = id;
214 this.updateTelemetryOnChange(id, previousTool);
215 this.emit(this._currentTool + "-selected");
216 this.emit("select", this._currentTool);
220 * Log toolClosed and toolOpened events on telemetry.
222 * @param {String} currentToolId
223 * id of the tool being selected.
224 * @param {String} previousToolId
225 * id of the previously selected tool.
227 updateTelemetryOnChange(currentToolId, previousToolId) {
228 if (currentToolId === previousToolId || !this._telemetry) {
229 // Skip telemetry if the tool id did not change or telemetry is unavailable.
233 currentToolId = this.getTelemetryPanelNameOrOther(currentToolId);
235 if (previousToolId) {
236 previousToolId = this.getTelemetryPanelNameOrOther(previousToolId);
237 this._telemetry.toolClosed(previousToolId, this);
239 this._telemetry.recordEvent("sidepanel_changed", "inspector", null, {
240 oldpanel: previousToolId,
241 newpanel: currentToolId,
242 os: this._telemetry.osNameAndVersion,
245 this._telemetry.toolOpened(currentToolId, this);
249 * Returns a panel id in the case of built in panels or "other" in the case of
250 * third party panels. This is necessary due to limitations in addon id strings,
251 * the permitted length of event telemetry property values and what we actually
252 * want to see in our telemetry.
255 * The panel id we would like to process.
257 getTelemetryPanelNameOrOther(id) {
258 if (!this._toolNames) {
259 // Get all built in tool ids. We identify third party tool ids by checking
260 // for a "-", which shows it originates from an addon.
261 const ids = this._tabbar.state.tabs.map(({ id: toolId }) => {
262 return toolId.includes("-") ? "other" : toolId;
265 this._toolNames = new Set(ids);
268 if (!this._toolNames.has(id)) {
279 * The sidebar tab id to select.
282 this._tabbox.hidden = false;
284 // If an id is given, select the corresponding sidebar tab.
296 this._tabbox.hidden = true;
305 if (this._destroyed) {
308 this._destroyed = true;
310 this.emit("destroy");
312 if (this._currentTool && this._telemetry) {
313 this._telemetry.toolClosed(this._currentTool, this);
316 this._toolPanel.emit("sidebar-destroyed", this);
318 this.ReactDOM.unmountComponentAtNode(this._tabbox);
322 this._telemetry = null;
323 this._panelDoc = null;
324 this._toolPanel = null;