michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Cu = Components.utils; michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); michael@0: const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); michael@0: michael@0: const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); michael@0: const {require} = devtools; michael@0: michael@0: const {ConnectionManager, Connection} michael@0: = require("devtools/client/connection-manager"); michael@0: const {getDeviceFront} = require("devtools/server/actors/device"); michael@0: const {getTargetForApp, launchApp, closeApp} michael@0: = require("devtools/app-actor-front"); michael@0: const DeviceStore = require("devtools/app-manager/device-store"); michael@0: const WebappsStore = require("devtools/app-manager/webapps-store"); michael@0: const promise = require("devtools/toolkit/deprecated-sync-thenables"); michael@0: const DEFAULT_APP_ICON = "chrome://browser/skin/devtools/app-manager/default-app-icon.png"; michael@0: michael@0: window.addEventListener("message", function(event) { michael@0: try { michael@0: let message = JSON.parse(event.data); michael@0: if (message.name == "connection") { michael@0: let cid = parseInt(message.cid); michael@0: for (let c of ConnectionManager.connections) { michael@0: if (c.uid == cid) { michael@0: UI.connection = c; michael@0: UI.onNewConnection(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } catch(e) { michael@0: Cu.reportError(e); michael@0: } michael@0: }); michael@0: michael@0: window.addEventListener("unload", function onUnload() { michael@0: window.removeEventListener("unload", onUnload); michael@0: UI.destroy(); michael@0: }); michael@0: michael@0: let UI = { michael@0: init: function() { michael@0: this.showFooterIfNeeded(); michael@0: this.setTab("apps"); michael@0: if (this.connection) { michael@0: this.onNewConnection(); michael@0: } else { michael@0: this.hide(); michael@0: } michael@0: }, michael@0: michael@0: destroy: function() { michael@0: if (this.connection) { michael@0: this.connection.off(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange); michael@0: } michael@0: if (this.store) { michael@0: this.store.destroy(); michael@0: } michael@0: if (this.template) { michael@0: this.template.destroy(); michael@0: } michael@0: }, michael@0: michael@0: showFooterIfNeeded: function() { michael@0: let footer = document.querySelector("#connection-footer"); michael@0: if (window.parent == window) { michael@0: // We're alone. Let's add a footer. michael@0: footer.removeAttribute("hidden"); michael@0: footer.src = "chrome://browser/content/devtools/app-manager/connection-footer.xhtml"; michael@0: } else { michael@0: footer.setAttribute("hidden", "true"); michael@0: } michael@0: }, michael@0: michael@0: hide: function() { michael@0: document.body.classList.add("notconnected"); michael@0: }, michael@0: michael@0: show: function() { michael@0: document.body.classList.remove("notconnected"); michael@0: }, michael@0: michael@0: onNewConnection: function() { michael@0: this.connection.on(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange); michael@0: michael@0: this.store = Utils.mergeStores({ michael@0: "device": new DeviceStore(this.connection), michael@0: "apps": new WebappsStore(this.connection), michael@0: }); michael@0: michael@0: if (this.template) { michael@0: this.template.destroy(); michael@0: } michael@0: this.template = new Template(document.body, this.store, Utils.l10n); michael@0: michael@0: this.template.start(); michael@0: this._onConnectionStatusChange(); michael@0: }, michael@0: michael@0: setWallpaper: function(dataurl) { michael@0: document.getElementById("meta").style.backgroundImage = "url(" + dataurl + ")"; michael@0: }, michael@0: michael@0: _onConnectionStatusChange: function() { michael@0: if (this.connection.status != Connection.Status.CONNECTED) { michael@0: this.hide(); michael@0: this.listTabsResponse = null; michael@0: } else { michael@0: this.show(); michael@0: this.connection.client.listTabs( michael@0: response => { michael@0: this.listTabsResponse = response; michael@0: let front = getDeviceFront(this.connection.client, this.listTabsResponse); michael@0: front.getWallpaper().then(longstr => { michael@0: longstr.string().then(dataURL => { michael@0: longstr.release().then(null, Cu.reportError); michael@0: this.setWallpaper(dataURL); michael@0: }); michael@0: }); michael@0: if (Services.prefs.getBoolPref("devtools.chrome.enabled")) { michael@0: let rootButton = document.getElementById("root-actor-debug"); michael@0: if (response.consoleActor) { michael@0: rootButton.removeAttribute("hidden"); michael@0: } else { michael@0: rootButton.setAttribute("hidden", "true"); michael@0: } michael@0: } michael@0: } michael@0: ); michael@0: } michael@0: }, michael@0: michael@0: get connected() { return !!this.listTabsResponse; }, michael@0: michael@0: setTab: function(name) { michael@0: var tab = document.querySelector(".tab.selected"); michael@0: var panel = document.querySelector(".tabpanel.selected"); michael@0: michael@0: if (tab) tab.classList.remove("selected"); michael@0: if (panel) panel.classList.remove("selected"); michael@0: michael@0: var tab = document.querySelector(".tab." + name); michael@0: var panel = document.querySelector(".tabpanel." + name); michael@0: michael@0: if (tab) tab.classList.add("selected"); michael@0: if (panel) panel.classList.add("selected"); michael@0: }, michael@0: michael@0: openToolboxForRootActor: function() { michael@0: if (!this.connected) { michael@0: return; michael@0: } michael@0: michael@0: let options = { michael@0: form: this.listTabsResponse, michael@0: client: this.connection.client, michael@0: chrome: true michael@0: }; michael@0: devtools.TargetFactory.forRemoteTab(options).then((target) => { michael@0: top.UI.openAndShowToolboxForTarget(target, "Main process", null); michael@0: }); michael@0: }, michael@0: michael@0: openToolboxForApp: function(manifest) { michael@0: if (!this.connected) { michael@0: return; michael@0: } michael@0: michael@0: let app = this.store.object.apps.all.filter(a => a.manifestURL == manifest)[0]; michael@0: getTargetForApp(this.connection.client, michael@0: this.listTabsResponse.webappsActor, michael@0: manifest).then((target) => { michael@0: michael@0: top.UI.openAndShowToolboxForTarget(target, app.name, app.iconURL); michael@0: }, console.error); michael@0: }, michael@0: michael@0: _getTargetForTab: function (form) { michael@0: let options = { michael@0: form: form, michael@0: client: this.connection.client, michael@0: chrome: false michael@0: }; michael@0: let deferred = promise.defer(); michael@0: return devtools.TargetFactory.forRemoteTab(options); michael@0: }, michael@0: michael@0: openToolboxForTab: function (aNode) { michael@0: let index = Array.prototype.indexOf.apply( michael@0: aNode.parentNode.parentNode.parentNode.children, michael@0: [aNode.parentNode.parentNode]); michael@0: this.connection.client.listTabs( michael@0: response => { michael@0: let tab = response.tabs[index]; michael@0: this._getTargetForTab(tab).then(target => { michael@0: top.UI.openAndShowToolboxForTarget( michael@0: target, tab.title, DEFAULT_APP_ICON); michael@0: }, console.error); michael@0: } michael@0: ); michael@0: }, michael@0: michael@0: startApp: function(manifest) { michael@0: if (!this.connected) { michael@0: return promise.reject(); michael@0: } michael@0: return launchApp(this.connection.client, michael@0: this.listTabsResponse.webappsActor, michael@0: manifest); michael@0: }, michael@0: michael@0: stopApp: function(manifest) { michael@0: if (!this.connected) { michael@0: return promise.reject(); michael@0: } michael@0: return closeApp(this.connection.client, michael@0: this.listTabsResponse.webappsActor, michael@0: manifest); michael@0: }, michael@0: } michael@0: michael@0: // This must be bound immediately, as it might be used via the message listener michael@0: // before UI.init() has been called. michael@0: UI._onConnectionStatusChange = UI._onConnectionStatusChange.bind(UI);