1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/framework/sidebar.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,242 @@ 1.4 +/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +const {Cu} = require("chrome"); 1.11 + 1.12 +Cu.import("resource://gre/modules/Services.jsm"); 1.13 + 1.14 +var {Promise: promise} = require("resource://gre/modules/Promise.jsm"); 1.15 +var EventEmitter = require("devtools/toolkit/event-emitter"); 1.16 +var Telemetry = require("devtools/shared/telemetry"); 1.17 + 1.18 +const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.19 + 1.20 +/** 1.21 + * ToolSidebar provides methods to register tabs in the sidebar. 1.22 + * It's assumed that the sidebar contains a xul:tabbox. 1.23 + * 1.24 + * @param {Node} tabbox 1.25 + * <tabbox> node; 1.26 + * @param {ToolPanel} panel 1.27 + * Related ToolPanel instance; 1.28 + * @param {String} uid 1.29 + * Unique ID 1.30 + * @param {Boolean} showTabstripe 1.31 + * Show the tabs. 1.32 + */ 1.33 +function ToolSidebar(tabbox, panel, uid, showTabstripe=true) 1.34 +{ 1.35 + EventEmitter.decorate(this); 1.36 + 1.37 + this._tabbox = tabbox; 1.38 + this._uid = uid; 1.39 + this._panelDoc = this._tabbox.ownerDocument; 1.40 + this._toolPanel = panel; 1.41 + 1.42 + try { 1.43 + this._width = Services.prefs.getIntPref("devtools.toolsidebar-width." + this._uid); 1.44 + } catch(e) {} 1.45 + 1.46 + this._telemetry = new Telemetry(); 1.47 + 1.48 + this._tabbox.tabpanels.addEventListener("select", this, true); 1.49 + 1.50 + this._tabs = new Map(); 1.51 + 1.52 + if (!showTabstripe) { 1.53 + this._tabbox.setAttribute("hidetabs", "true"); 1.54 + } 1.55 +} 1.56 + 1.57 +exports.ToolSidebar = ToolSidebar; 1.58 + 1.59 +ToolSidebar.prototype = { 1.60 + /** 1.61 + * Register a tab. A tab is a document. 1.62 + * The document must have a title, which will be used as the name of the tab. 1.63 + * 1.64 + * @param {string} tab uniq id 1.65 + * @param {string} url 1.66 + */ 1.67 + addTab: function ToolSidebar_addTab(id, url, selected=false) { 1.68 + let iframe = this._panelDoc.createElementNS(XULNS, "iframe"); 1.69 + iframe.className = "iframe-" + id; 1.70 + iframe.setAttribute("flex", "1"); 1.71 + iframe.setAttribute("src", url); 1.72 + iframe.tooltip = "aHTMLTooltip"; 1.73 + 1.74 + let tab = this._tabbox.tabs.appendItem(); 1.75 + tab.setAttribute("label", ""); // Avoid showing "undefined" while the tab is loading 1.76 + 1.77 + let onIFrameLoaded = function() { 1.78 + tab.setAttribute("label", iframe.contentDocument.title); 1.79 + iframe.removeEventListener("load", onIFrameLoaded, true); 1.80 + if ("setPanel" in iframe.contentWindow) { 1.81 + iframe.contentWindow.setPanel(this._toolPanel, iframe); 1.82 + } 1.83 + this.emit(id + "-ready"); 1.84 + }.bind(this); 1.85 + 1.86 + iframe.addEventListener("load", onIFrameLoaded, true); 1.87 + 1.88 + let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel"); 1.89 + tabpanel.setAttribute("id", "sidebar-panel-" + id); 1.90 + tabpanel.appendChild(iframe); 1.91 + this._tabbox.tabpanels.appendChild(tabpanel); 1.92 + 1.93 + this._tooltip = this._panelDoc.createElementNS(XULNS, "tooltip"); 1.94 + this._tooltip.id = "aHTMLTooltip"; 1.95 + tabpanel.appendChild(this._tooltip); 1.96 + this._tooltip.page = true; 1.97 + 1.98 + tab.linkedPanel = "sidebar-panel-" + id; 1.99 + 1.100 + // We store the index of this tab. 1.101 + this._tabs.set(id, tab); 1.102 + 1.103 + if (selected) { 1.104 + // For some reason I don't understand, if we call this.select in this 1.105 + // event loop (after inserting the tab), the tab will never get the 1.106 + // the "selected" attribute set to true. 1.107 + this._panelDoc.defaultView.setTimeout(function() { 1.108 + this.select(id); 1.109 + }.bind(this), 10); 1.110 + } 1.111 + 1.112 + this.emit("new-tab-registered", id); 1.113 + }, 1.114 + 1.115 + /** 1.116 + * Select a specific tab. 1.117 + */ 1.118 + select: function ToolSidebar_select(id) { 1.119 + let tab = this._tabs.get(id); 1.120 + if (tab) { 1.121 + this._tabbox.selectedTab = tab; 1.122 + } 1.123 + }, 1.124 + 1.125 + /** 1.126 + * Return the id of the selected tab. 1.127 + */ 1.128 + getCurrentTabID: function ToolSidebar_getCurrentTabID() { 1.129 + let currentID = null; 1.130 + for (let [id, tab] of this._tabs) { 1.131 + if (this._tabbox.tabs.selectedItem == tab) { 1.132 + currentID = id; 1.133 + break; 1.134 + } 1.135 + } 1.136 + return currentID; 1.137 + }, 1.138 + 1.139 + /** 1.140 + * Returns the requested tab based on the id. 1.141 + * 1.142 + * @param String id 1.143 + * unique id of the requested tab. 1.144 + */ 1.145 + getTab: function ToolSidebar_getTab(id) { 1.146 + return this._tabbox.tabpanels.querySelector("#sidebar-panel-" + id); 1.147 + }, 1.148 + 1.149 + /** 1.150 + * Event handler. 1.151 + */ 1.152 + handleEvent: function ToolSidebar_eventHandler(event) { 1.153 + if (event.type == "select") { 1.154 + if (this._currentTool == this.getCurrentTabID()) { 1.155 + // Tool hasn't changed. 1.156 + return; 1.157 + } 1.158 + 1.159 + let previousTool = this._currentTool; 1.160 + this._currentTool = this.getCurrentTabID(); 1.161 + if (previousTool) { 1.162 + this._telemetry.toolClosed(previousTool); 1.163 + this.emit(previousTool + "-unselected"); 1.164 + } 1.165 + 1.166 + this._telemetry.toolOpened(this._currentTool); 1.167 + this.emit(this._currentTool + "-selected"); 1.168 + this.emit("select", this._currentTool); 1.169 + } 1.170 + }, 1.171 + 1.172 + /** 1.173 + * Toggle sidebar's visibility state. 1.174 + */ 1.175 + toggle: function ToolSidebar_toggle() { 1.176 + if (this._tabbox.hasAttribute("hidden")) { 1.177 + this.show(); 1.178 + } else { 1.179 + this.hide(); 1.180 + } 1.181 + }, 1.182 + 1.183 + /** 1.184 + * Show the sidebar. 1.185 + */ 1.186 + show: function ToolSidebar_show() { 1.187 + if (this._width) { 1.188 + this._tabbox.width = this._width; 1.189 + } 1.190 + this._tabbox.removeAttribute("hidden"); 1.191 + }, 1.192 + 1.193 + /** 1.194 + * Show the sidebar. 1.195 + */ 1.196 + hide: function ToolSidebar_hide() { 1.197 + Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width); 1.198 + this._tabbox.setAttribute("hidden", "true"); 1.199 + }, 1.200 + 1.201 + /** 1.202 + * Return the window containing the tab content. 1.203 + */ 1.204 + getWindowForTab: function ToolSidebar_getWindowForTab(id) { 1.205 + if (!this._tabs.has(id)) { 1.206 + return null; 1.207 + } 1.208 + 1.209 + let panel = this._panelDoc.getElementById(this._tabs.get(id).linkedPanel); 1.210 + return panel.firstChild.contentWindow; 1.211 + }, 1.212 + 1.213 + /** 1.214 + * Clean-up. 1.215 + */ 1.216 + destroy: function ToolSidebar_destroy() { 1.217 + if (this._destroyed) { 1.218 + return promise.resolve(null); 1.219 + } 1.220 + this._destroyed = true; 1.221 + 1.222 + Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width); 1.223 + 1.224 + this._tabbox.tabpanels.removeEventListener("select", this, true); 1.225 + 1.226 + while (this._tabbox.tabpanels.hasChildNodes()) { 1.227 + this._tabbox.tabpanels.removeChild(this._tabbox.tabpanels.firstChild); 1.228 + } 1.229 + 1.230 + while (this._tabbox.tabs.hasChildNodes()) { 1.231 + this._tabbox.tabs.removeChild(this._tabbox.tabs.firstChild); 1.232 + } 1.233 + 1.234 + if (this._currentTool) { 1.235 + this._telemetry.toolClosed(this._currentTool); 1.236 + } 1.237 + 1.238 + this._tabs = null; 1.239 + this._tabbox = null; 1.240 + this._panelDoc = null; 1.241 + this._toolPanel = null; 1.242 + 1.243 + return promise.resolve(null); 1.244 + }, 1.245 +}