123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
- /* 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;
- })
- };
|