|
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/. */ |
|
4 |
|
5 const Cu = Components.utils; |
|
6 Cu.import("resource://gre/modules/Services.jsm"); |
|
7 Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); |
|
8 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); |
|
9 |
|
10 const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); |
|
11 const {require} = devtools; |
|
12 |
|
13 const {ConnectionManager, Connection} |
|
14 = require("devtools/client/connection-manager"); |
|
15 const {getDeviceFront} = require("devtools/server/actors/device"); |
|
16 const {getTargetForApp, launchApp, closeApp} |
|
17 = require("devtools/app-actor-front"); |
|
18 const DeviceStore = require("devtools/app-manager/device-store"); |
|
19 const WebappsStore = require("devtools/app-manager/webapps-store"); |
|
20 const promise = require("devtools/toolkit/deprecated-sync-thenables"); |
|
21 const DEFAULT_APP_ICON = "chrome://browser/skin/devtools/app-manager/default-app-icon.png"; |
|
22 |
|
23 window.addEventListener("message", function(event) { |
|
24 try { |
|
25 let message = JSON.parse(event.data); |
|
26 if (message.name == "connection") { |
|
27 let cid = parseInt(message.cid); |
|
28 for (let c of ConnectionManager.connections) { |
|
29 if (c.uid == cid) { |
|
30 UI.connection = c; |
|
31 UI.onNewConnection(); |
|
32 break; |
|
33 } |
|
34 } |
|
35 } |
|
36 } catch(e) { |
|
37 Cu.reportError(e); |
|
38 } |
|
39 }); |
|
40 |
|
41 window.addEventListener("unload", function onUnload() { |
|
42 window.removeEventListener("unload", onUnload); |
|
43 UI.destroy(); |
|
44 }); |
|
45 |
|
46 let UI = { |
|
47 init: function() { |
|
48 this.showFooterIfNeeded(); |
|
49 this.setTab("apps"); |
|
50 if (this.connection) { |
|
51 this.onNewConnection(); |
|
52 } else { |
|
53 this.hide(); |
|
54 } |
|
55 }, |
|
56 |
|
57 destroy: function() { |
|
58 if (this.connection) { |
|
59 this.connection.off(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange); |
|
60 } |
|
61 if (this.store) { |
|
62 this.store.destroy(); |
|
63 } |
|
64 if (this.template) { |
|
65 this.template.destroy(); |
|
66 } |
|
67 }, |
|
68 |
|
69 showFooterIfNeeded: function() { |
|
70 let footer = document.querySelector("#connection-footer"); |
|
71 if (window.parent == window) { |
|
72 // We're alone. Let's add a footer. |
|
73 footer.removeAttribute("hidden"); |
|
74 footer.src = "chrome://browser/content/devtools/app-manager/connection-footer.xhtml"; |
|
75 } else { |
|
76 footer.setAttribute("hidden", "true"); |
|
77 } |
|
78 }, |
|
79 |
|
80 hide: function() { |
|
81 document.body.classList.add("notconnected"); |
|
82 }, |
|
83 |
|
84 show: function() { |
|
85 document.body.classList.remove("notconnected"); |
|
86 }, |
|
87 |
|
88 onNewConnection: function() { |
|
89 this.connection.on(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange); |
|
90 |
|
91 this.store = Utils.mergeStores({ |
|
92 "device": new DeviceStore(this.connection), |
|
93 "apps": new WebappsStore(this.connection), |
|
94 }); |
|
95 |
|
96 if (this.template) { |
|
97 this.template.destroy(); |
|
98 } |
|
99 this.template = new Template(document.body, this.store, Utils.l10n); |
|
100 |
|
101 this.template.start(); |
|
102 this._onConnectionStatusChange(); |
|
103 }, |
|
104 |
|
105 setWallpaper: function(dataurl) { |
|
106 document.getElementById("meta").style.backgroundImage = "url(" + dataurl + ")"; |
|
107 }, |
|
108 |
|
109 _onConnectionStatusChange: function() { |
|
110 if (this.connection.status != Connection.Status.CONNECTED) { |
|
111 this.hide(); |
|
112 this.listTabsResponse = null; |
|
113 } else { |
|
114 this.show(); |
|
115 this.connection.client.listTabs( |
|
116 response => { |
|
117 this.listTabsResponse = response; |
|
118 let front = getDeviceFront(this.connection.client, this.listTabsResponse); |
|
119 front.getWallpaper().then(longstr => { |
|
120 longstr.string().then(dataURL => { |
|
121 longstr.release().then(null, Cu.reportError); |
|
122 this.setWallpaper(dataURL); |
|
123 }); |
|
124 }); |
|
125 if (Services.prefs.getBoolPref("devtools.chrome.enabled")) { |
|
126 let rootButton = document.getElementById("root-actor-debug"); |
|
127 if (response.consoleActor) { |
|
128 rootButton.removeAttribute("hidden"); |
|
129 } else { |
|
130 rootButton.setAttribute("hidden", "true"); |
|
131 } |
|
132 } |
|
133 } |
|
134 ); |
|
135 } |
|
136 }, |
|
137 |
|
138 get connected() { return !!this.listTabsResponse; }, |
|
139 |
|
140 setTab: function(name) { |
|
141 var tab = document.querySelector(".tab.selected"); |
|
142 var panel = document.querySelector(".tabpanel.selected"); |
|
143 |
|
144 if (tab) tab.classList.remove("selected"); |
|
145 if (panel) panel.classList.remove("selected"); |
|
146 |
|
147 var tab = document.querySelector(".tab." + name); |
|
148 var panel = document.querySelector(".tabpanel." + name); |
|
149 |
|
150 if (tab) tab.classList.add("selected"); |
|
151 if (panel) panel.classList.add("selected"); |
|
152 }, |
|
153 |
|
154 openToolboxForRootActor: function() { |
|
155 if (!this.connected) { |
|
156 return; |
|
157 } |
|
158 |
|
159 let options = { |
|
160 form: this.listTabsResponse, |
|
161 client: this.connection.client, |
|
162 chrome: true |
|
163 }; |
|
164 devtools.TargetFactory.forRemoteTab(options).then((target) => { |
|
165 top.UI.openAndShowToolboxForTarget(target, "Main process", null); |
|
166 }); |
|
167 }, |
|
168 |
|
169 openToolboxForApp: function(manifest) { |
|
170 if (!this.connected) { |
|
171 return; |
|
172 } |
|
173 |
|
174 let app = this.store.object.apps.all.filter(a => a.manifestURL == manifest)[0]; |
|
175 getTargetForApp(this.connection.client, |
|
176 this.listTabsResponse.webappsActor, |
|
177 manifest).then((target) => { |
|
178 |
|
179 top.UI.openAndShowToolboxForTarget(target, app.name, app.iconURL); |
|
180 }, console.error); |
|
181 }, |
|
182 |
|
183 _getTargetForTab: function (form) { |
|
184 let options = { |
|
185 form: form, |
|
186 client: this.connection.client, |
|
187 chrome: false |
|
188 }; |
|
189 let deferred = promise.defer(); |
|
190 return devtools.TargetFactory.forRemoteTab(options); |
|
191 }, |
|
192 |
|
193 openToolboxForTab: function (aNode) { |
|
194 let index = Array.prototype.indexOf.apply( |
|
195 aNode.parentNode.parentNode.parentNode.children, |
|
196 [aNode.parentNode.parentNode]); |
|
197 this.connection.client.listTabs( |
|
198 response => { |
|
199 let tab = response.tabs[index]; |
|
200 this._getTargetForTab(tab).then(target => { |
|
201 top.UI.openAndShowToolboxForTarget( |
|
202 target, tab.title, DEFAULT_APP_ICON); |
|
203 }, console.error); |
|
204 } |
|
205 ); |
|
206 }, |
|
207 |
|
208 startApp: function(manifest) { |
|
209 if (!this.connected) { |
|
210 return promise.reject(); |
|
211 } |
|
212 return launchApp(this.connection.client, |
|
213 this.listTabsResponse.webappsActor, |
|
214 manifest); |
|
215 }, |
|
216 |
|
217 stopApp: function(manifest) { |
|
218 if (!this.connected) { |
|
219 return promise.reject(); |
|
220 } |
|
221 return closeApp(this.connection.client, |
|
222 this.listTabsResponse.webappsActor, |
|
223 manifest); |
|
224 }, |
|
225 } |
|
226 |
|
227 // This must be bound immediately, as it might be used via the message listener |
|
228 // before UI.init() has been called. |
|
229 UI._onConnectionStatusChange = UI._onConnectionStatusChange.bind(UI); |