|
1 /* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 const {Cu} = require("chrome"); |
|
8 |
|
9 Cu.import("resource://gre/modules/Services.jsm"); |
|
10 |
|
11 var {Promise: promise} = require("resource://gre/modules/Promise.jsm"); |
|
12 var EventEmitter = require("devtools/toolkit/event-emitter"); |
|
13 var Telemetry = require("devtools/shared/telemetry"); |
|
14 |
|
15 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
16 |
|
17 /** |
|
18 * ToolSidebar provides methods to register tabs in the sidebar. |
|
19 * It's assumed that the sidebar contains a xul:tabbox. |
|
20 * |
|
21 * @param {Node} tabbox |
|
22 * <tabbox> node; |
|
23 * @param {ToolPanel} panel |
|
24 * Related ToolPanel instance; |
|
25 * @param {String} uid |
|
26 * Unique ID |
|
27 * @param {Boolean} showTabstripe |
|
28 * Show the tabs. |
|
29 */ |
|
30 function ToolSidebar(tabbox, panel, uid, showTabstripe=true) |
|
31 { |
|
32 EventEmitter.decorate(this); |
|
33 |
|
34 this._tabbox = tabbox; |
|
35 this._uid = uid; |
|
36 this._panelDoc = this._tabbox.ownerDocument; |
|
37 this._toolPanel = panel; |
|
38 |
|
39 try { |
|
40 this._width = Services.prefs.getIntPref("devtools.toolsidebar-width." + this._uid); |
|
41 } catch(e) {} |
|
42 |
|
43 this._telemetry = new Telemetry(); |
|
44 |
|
45 this._tabbox.tabpanels.addEventListener("select", this, true); |
|
46 |
|
47 this._tabs = new Map(); |
|
48 |
|
49 if (!showTabstripe) { |
|
50 this._tabbox.setAttribute("hidetabs", "true"); |
|
51 } |
|
52 } |
|
53 |
|
54 exports.ToolSidebar = ToolSidebar; |
|
55 |
|
56 ToolSidebar.prototype = { |
|
57 /** |
|
58 * Register a tab. A tab is a document. |
|
59 * The document must have a title, which will be used as the name of the tab. |
|
60 * |
|
61 * @param {string} tab uniq id |
|
62 * @param {string} url |
|
63 */ |
|
64 addTab: function ToolSidebar_addTab(id, url, selected=false) { |
|
65 let iframe = this._panelDoc.createElementNS(XULNS, "iframe"); |
|
66 iframe.className = "iframe-" + id; |
|
67 iframe.setAttribute("flex", "1"); |
|
68 iframe.setAttribute("src", url); |
|
69 iframe.tooltip = "aHTMLTooltip"; |
|
70 |
|
71 let tab = this._tabbox.tabs.appendItem(); |
|
72 tab.setAttribute("label", ""); // Avoid showing "undefined" while the tab is loading |
|
73 |
|
74 let onIFrameLoaded = function() { |
|
75 tab.setAttribute("label", iframe.contentDocument.title); |
|
76 iframe.removeEventListener("load", onIFrameLoaded, true); |
|
77 if ("setPanel" in iframe.contentWindow) { |
|
78 iframe.contentWindow.setPanel(this._toolPanel, iframe); |
|
79 } |
|
80 this.emit(id + "-ready"); |
|
81 }.bind(this); |
|
82 |
|
83 iframe.addEventListener("load", onIFrameLoaded, true); |
|
84 |
|
85 let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel"); |
|
86 tabpanel.setAttribute("id", "sidebar-panel-" + id); |
|
87 tabpanel.appendChild(iframe); |
|
88 this._tabbox.tabpanels.appendChild(tabpanel); |
|
89 |
|
90 this._tooltip = this._panelDoc.createElementNS(XULNS, "tooltip"); |
|
91 this._tooltip.id = "aHTMLTooltip"; |
|
92 tabpanel.appendChild(this._tooltip); |
|
93 this._tooltip.page = true; |
|
94 |
|
95 tab.linkedPanel = "sidebar-panel-" + id; |
|
96 |
|
97 // We store the index of this tab. |
|
98 this._tabs.set(id, tab); |
|
99 |
|
100 if (selected) { |
|
101 // For some reason I don't understand, if we call this.select in this |
|
102 // event loop (after inserting the tab), the tab will never get the |
|
103 // the "selected" attribute set to true. |
|
104 this._panelDoc.defaultView.setTimeout(function() { |
|
105 this.select(id); |
|
106 }.bind(this), 10); |
|
107 } |
|
108 |
|
109 this.emit("new-tab-registered", id); |
|
110 }, |
|
111 |
|
112 /** |
|
113 * Select a specific tab. |
|
114 */ |
|
115 select: function ToolSidebar_select(id) { |
|
116 let tab = this._tabs.get(id); |
|
117 if (tab) { |
|
118 this._tabbox.selectedTab = tab; |
|
119 } |
|
120 }, |
|
121 |
|
122 /** |
|
123 * Return the id of the selected tab. |
|
124 */ |
|
125 getCurrentTabID: function ToolSidebar_getCurrentTabID() { |
|
126 let currentID = null; |
|
127 for (let [id, tab] of this._tabs) { |
|
128 if (this._tabbox.tabs.selectedItem == tab) { |
|
129 currentID = id; |
|
130 break; |
|
131 } |
|
132 } |
|
133 return currentID; |
|
134 }, |
|
135 |
|
136 /** |
|
137 * Returns the requested tab based on the id. |
|
138 * |
|
139 * @param String id |
|
140 * unique id of the requested tab. |
|
141 */ |
|
142 getTab: function ToolSidebar_getTab(id) { |
|
143 return this._tabbox.tabpanels.querySelector("#sidebar-panel-" + id); |
|
144 }, |
|
145 |
|
146 /** |
|
147 * Event handler. |
|
148 */ |
|
149 handleEvent: function ToolSidebar_eventHandler(event) { |
|
150 if (event.type == "select") { |
|
151 if (this._currentTool == this.getCurrentTabID()) { |
|
152 // Tool hasn't changed. |
|
153 return; |
|
154 } |
|
155 |
|
156 let previousTool = this._currentTool; |
|
157 this._currentTool = this.getCurrentTabID(); |
|
158 if (previousTool) { |
|
159 this._telemetry.toolClosed(previousTool); |
|
160 this.emit(previousTool + "-unselected"); |
|
161 } |
|
162 |
|
163 this._telemetry.toolOpened(this._currentTool); |
|
164 this.emit(this._currentTool + "-selected"); |
|
165 this.emit("select", this._currentTool); |
|
166 } |
|
167 }, |
|
168 |
|
169 /** |
|
170 * Toggle sidebar's visibility state. |
|
171 */ |
|
172 toggle: function ToolSidebar_toggle() { |
|
173 if (this._tabbox.hasAttribute("hidden")) { |
|
174 this.show(); |
|
175 } else { |
|
176 this.hide(); |
|
177 } |
|
178 }, |
|
179 |
|
180 /** |
|
181 * Show the sidebar. |
|
182 */ |
|
183 show: function ToolSidebar_show() { |
|
184 if (this._width) { |
|
185 this._tabbox.width = this._width; |
|
186 } |
|
187 this._tabbox.removeAttribute("hidden"); |
|
188 }, |
|
189 |
|
190 /** |
|
191 * Show the sidebar. |
|
192 */ |
|
193 hide: function ToolSidebar_hide() { |
|
194 Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width); |
|
195 this._tabbox.setAttribute("hidden", "true"); |
|
196 }, |
|
197 |
|
198 /** |
|
199 * Return the window containing the tab content. |
|
200 */ |
|
201 getWindowForTab: function ToolSidebar_getWindowForTab(id) { |
|
202 if (!this._tabs.has(id)) { |
|
203 return null; |
|
204 } |
|
205 |
|
206 let panel = this._panelDoc.getElementById(this._tabs.get(id).linkedPanel); |
|
207 return panel.firstChild.contentWindow; |
|
208 }, |
|
209 |
|
210 /** |
|
211 * Clean-up. |
|
212 */ |
|
213 destroy: function ToolSidebar_destroy() { |
|
214 if (this._destroyed) { |
|
215 return promise.resolve(null); |
|
216 } |
|
217 this._destroyed = true; |
|
218 |
|
219 Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width); |
|
220 |
|
221 this._tabbox.tabpanels.removeEventListener("select", this, true); |
|
222 |
|
223 while (this._tabbox.tabpanels.hasChildNodes()) { |
|
224 this._tabbox.tabpanels.removeChild(this._tabbox.tabpanels.firstChild); |
|
225 } |
|
226 |
|
227 while (this._tabbox.tabs.hasChildNodes()) { |
|
228 this._tabbox.tabs.removeChild(this._tabbox.tabs.firstChild); |
|
229 } |
|
230 |
|
231 if (this._currentTool) { |
|
232 this._telemetry.toolClosed(this._currentTool); |
|
233 } |
|
234 |
|
235 this._tabs = null; |
|
236 this._tabbox = null; |
|
237 this._panelDoc = null; |
|
238 this._toolPanel = null; |
|
239 |
|
240 return promise.resolve(null); |
|
241 }, |
|
242 } |