summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/toolsidebar.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/inspector/toolsidebar.js')
-rw-r--r--devtools/client/inspector/toolsidebar.js325
1 files changed, 325 insertions, 0 deletions
diff --git a/devtools/client/inspector/toolsidebar.js b/devtools/client/inspector/toolsidebar.js
new file mode 100644
index 000000000..d013b7b84
--- /dev/null
+++ b/devtools/client/inspector/toolsidebar.js
@@ -0,0 +1,325 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var EventEmitter = require("devtools/shared/event-emitter");
+var Telemetry = require("devtools/client/shared/telemetry");
+var { Task } = require("devtools/shared/task");
+
+/**
+ * This object represents replacement for ToolSidebar
+ * implemented in devtools/client/framework/sidebar.js module
+ *
+ * This new component is part of devtools.html aimed at
+ * removing XUL and use HTML for entire DevTools UI.
+ * There are currently two implementation of the side bar since
+ * the `sidebar.js` module (mentioned above) is still used by
+ * other panels.
+ * As soon as all panels are using this HTML based
+ * implementation it can be removed.
+ */
+function ToolSidebar(tabbox, panel, uid, options = {}) {
+ EventEmitter.decorate(this);
+
+ this._tabbox = tabbox;
+ this._uid = uid;
+ this._panelDoc = this._tabbox.ownerDocument;
+ this._toolPanel = panel;
+ this._options = options;
+
+ if (!options.disableTelemetry) {
+ this._telemetry = new Telemetry();
+ }
+
+ this._tabs = [];
+
+ if (this._options.hideTabstripe) {
+ this._tabbox.setAttribute("hidetabs", "true");
+ }
+
+ this.render();
+
+ this._toolPanel.emit("sidebar-created", this);
+}
+
+exports.ToolSidebar = ToolSidebar;
+
+ToolSidebar.prototype = {
+ TABPANEL_ID_PREFIX: "sidebar-panel-",
+
+ // React
+
+ get React() {
+ return this._toolPanel.React;
+ },
+
+ get ReactDOM() {
+ return this._toolPanel.ReactDOM;
+ },
+
+ get browserRequire() {
+ return this._toolPanel.browserRequire;
+ },
+
+ get InspectorTabPanel() {
+ return this._toolPanel.InspectorTabPanel;
+ },
+
+ // Rendering
+
+ render: function () {
+ let Tabbar = this.React.createFactory(this.browserRequire(
+ "devtools/client/shared/components/tabs/tabbar"));
+
+ let sidebar = Tabbar({
+ toolbox: this._toolPanel._toolbox,
+ showAllTabsMenu: true,
+ onSelect: this.handleSelectionChange.bind(this),
+ });
+
+ this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
+ },
+
+ /**
+ * Register a side-panel tab.
+ *
+ * @param {string} tab uniq id
+ * @param {string} title tab title
+ * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
+ * @param {boolean} selected true if the panel should be selected
+ */
+ addTab: function (id, title, panel, selected) {
+ this._tabbar.addTab(id, title, selected, panel);
+ this.emit("new-tab-registered", id);
+ },
+
+ /**
+ * Helper API for adding side-panels that use existing DOM nodes
+ * (defined within inspector.xhtml) as the content.
+ *
+ * @param {string} tab uniq id
+ * @param {string} title tab title
+ * @param {boolean} selected true if the panel should be selected
+ */
+ addExistingTab: function (id, title, selected) {
+ let panel = this.InspectorTabPanel({
+ id: id,
+ idPrefix: this.TABPANEL_ID_PREFIX,
+ key: id,
+ title: title,
+ });
+
+ this.addTab(id, title, panel, selected);
+ },
+
+ /**
+ * Helper API for adding side-panels that use existing <iframe> nodes
+ * (defined within inspector.xhtml) as the content.
+ * The document must have a title, which will be used as the name of the tab.
+ *
+ * @param {string} tab uniq id
+ * @param {string} title tab title
+ * @param {string} url
+ * @param {boolean} selected true if the panel should be selected
+ */
+ addFrameTab: function (id, title, url, selected) {
+ let panel = this.InspectorTabPanel({
+ id: id,
+ idPrefix: this.TABPANEL_ID_PREFIX,
+ key: id,
+ title: title,
+ url: url,
+ onMount: this.onSidePanelMounted.bind(this),
+ });
+
+ this.addTab(id, title, panel, selected);
+ },
+
+ onSidePanelMounted: function (content, props) {
+ let iframe = content.querySelector("iframe");
+ if (!iframe || iframe.getAttribute("src")) {
+ return;
+ }
+
+ let onIFrameLoaded = (event) => {
+ iframe.removeEventListener("load", onIFrameLoaded, true);
+
+ let doc = event.target;
+ let win = doc.defaultView;
+ if ("setPanel" in win) {
+ win.setPanel(this._toolPanel, iframe);
+ }
+ this.emit(props.id + "-ready");
+ };
+
+ iframe.addEventListener("load", onIFrameLoaded, true);
+ iframe.setAttribute("src", props.url);
+ },
+
+ /**
+ * Remove an existing tab.
+ * @param {String} tabId The ID of the tab that was used to register it, or
+ * the tab id attribute value if the tab existed before the sidebar
+ * got created.
+ * @param {String} tabPanelId Optional. If provided, this ID will be used
+ * instead of the tabId to retrieve and remove the corresponding <tabpanel>
+ */
+ removeTab: Task.async(function* (tabId, tabPanelId) {
+ this._tabbar.removeTab(tabId);
+
+ let win = this.getWindowForTab(tabId);
+ if (win && ("destroy" in win)) {
+ yield win.destroy();
+ }
+
+ this.emit("tab-unregistered", tabId);
+ }),
+
+ /**
+ * Show or hide a specific tab.
+ * @param {Boolean} isVisible True to show the tab/tabpanel, False to hide it.
+ * @param {String} id The ID of the tab to be hidden.
+ */
+ toggleTab: function (isVisible, id) {
+ this._tabbar.toggleTab(id, isVisible);
+ },
+
+ /**
+ * Select a specific tab.
+ */
+ select: function (id) {
+ this._tabbar.select(id);
+ },
+
+ /**
+ * Return the id of the selected tab.
+ */
+ getCurrentTabID: function () {
+ return this._currentTool;
+ },
+
+ /**
+ * Returns the requested tab panel based on the id.
+ * @param {String} id
+ * @return {DOMNode}
+ */
+ getTabPanel: function (id) {
+ // Search with and without the ID prefix as there might have been existing
+ // tabpanels by the time the sidebar got created
+ return this._panelDoc.querySelector("#" +
+ this.TABPANEL_ID_PREFIX + id + ", #" + id);
+ },
+
+ /**
+ * Event handler.
+ */
+ handleSelectionChange: function (id) {
+ if (this._destroyed) {
+ return;
+ }
+
+ let previousTool = this._currentTool;
+ if (previousTool) {
+ if (this._telemetry) {
+ this._telemetry.toolClosed(previousTool);
+ }
+ this.emit(previousTool + "-unselected");
+ }
+
+ this._currentTool = id;
+
+ if (this._telemetry) {
+ this._telemetry.toolOpened(this._currentTool);
+ }
+
+ this.emit(this._currentTool + "-selected");
+ this.emit("select", this._currentTool);
+ },
+
+ /**
+ * Show the sidebar.
+ *
+ * @param {String} id
+ * The sidebar tab id to select.
+ */
+ show: function (id) {
+ this._tabbox.removeAttribute("hidden");
+
+ // If an id is given, select the corresponding sidebar tab and record the
+ // tool opened.
+ if (id) {
+ this._currentTool = id;
+
+ if (this._telemetry) {
+ this._telemetry.toolOpened(this._currentTool);
+ }
+ }
+
+ this.emit("show");
+ },
+
+ /**
+ * Show the sidebar.
+ */
+ hide: function () {
+ this._tabbox.setAttribute("hidden", "true");
+
+ this.emit("hide");
+ },
+
+ /**
+ * Return the window containing the tab content.
+ */
+ getWindowForTab: function (id) {
+ // Get the tabpanel and make sure it contains an iframe
+ let panel = this.getTabPanel(id);
+ if (!panel || !panel.firstElementChild || !panel.firstElementChild.contentWindow) {
+ return null;
+ }
+
+ return panel.firstElementChild.contentWindow;
+ },
+
+ /**
+ * Clean-up.
+ */
+ destroy: Task.async(function* () {
+ if (this._destroyed) {
+ return;
+ }
+ this._destroyed = true;
+
+ this.emit("destroy");
+
+ // Note that we check for the existence of this._tabbox.tabpanels at each
+ // step as the container window may have been closed by the time one of the
+ // panel's destroy promise resolves.
+ let tabpanels = [...this._tabbox.querySelectorAll(".tab-panel-box")];
+ for (let panel of tabpanels) {
+ let iframe = panel.querySelector("iframe");
+ if (!iframe) {
+ continue;
+ }
+ let win = iframe.contentWindow;
+ if (win && ("destroy" in win)) {
+ yield win.destroy();
+ }
+ panel.remove();
+ }
+
+ if (this._currentTool && this._telemetry) {
+ this._telemetry.toolClosed(this._currentTool);
+ }
+
+ this._toolPanel.emit("sidebar-destroyed", this);
+
+ this._tabs = null;
+ this._tabbox = null;
+ this._panelDoc = null;
+ this._toolPanel = null;
+ })
+};