michael@0: /* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ 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: "use strict"; michael@0: michael@0: const Cu = Components.utils; michael@0: Cu.import('resource://gre/modules/XPCOMUtils.jsm'); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/Task.jsm"); michael@0: Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); michael@0: let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); michael@0: let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); michael@0: let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); michael@0: michael@0: let gClient; michael@0: let gConnectionTimeout; michael@0: michael@0: XPCOMUtils.defineLazyGetter(window, 'l10n', function () { michael@0: return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties'); michael@0: }); michael@0: michael@0: /** michael@0: * Once DOM is ready, we prefil the host/port inputs with michael@0: * pref-stored values. michael@0: */ michael@0: window.addEventListener("DOMContentLoaded", function onDOMReady() { michael@0: window.removeEventListener("DOMContentLoaded", onDOMReady, true); michael@0: let host = Services.prefs.getCharPref("devtools.debugger.remote-host"); michael@0: let port = Services.prefs.getIntPref("devtools.debugger.remote-port"); michael@0: michael@0: if (host) { michael@0: document.getElementById("host").value = host; michael@0: } michael@0: michael@0: if (port) { michael@0: document.getElementById("port").value = port; michael@0: } michael@0: michael@0: let form = document.querySelector("#connection-form form"); michael@0: form.addEventListener("submit", function() { michael@0: window.submit(); michael@0: }); michael@0: }, true); michael@0: michael@0: /** michael@0: * Called when the "connect" button is clicked. michael@0: */ michael@0: function submit() { michael@0: // Show the "connecting" screen michael@0: document.body.classList.add("connecting"); michael@0: michael@0: // Save the host/port values michael@0: let host = document.getElementById("host").value; michael@0: Services.prefs.setCharPref("devtools.debugger.remote-host", host); michael@0: michael@0: let port = document.getElementById("port").value; michael@0: Services.prefs.setIntPref("devtools.debugger.remote-port", port); michael@0: michael@0: // Initiate the connection michael@0: let transport; michael@0: try { michael@0: transport = debuggerSocketConnect(host, port); michael@0: } catch(e) { michael@0: // Bug 921850: catch rare exception from debuggerSocketConnect michael@0: showError("unexpected"); michael@0: return; michael@0: } michael@0: gClient = new DebuggerClient(transport); michael@0: let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout"); michael@0: gConnectionTimeout = setTimeout(handleConnectionTimeout, delay); michael@0: gClient.connect(onConnectionReady); michael@0: } michael@0: michael@0: /** michael@0: * Connection is ready. List actors and build buttons. michael@0: */ michael@0: let onConnectionReady = Task.async(function*(aType, aTraits) { michael@0: clearTimeout(gConnectionTimeout); michael@0: michael@0: let deferred = promise.defer(); michael@0: gClient.listAddons(deferred.resolve); michael@0: let response = yield deferred.promise; michael@0: michael@0: let parent = document.getElementById("addonActors") michael@0: if (!response.error && response.addons.length > 0) { michael@0: // Add one entry for each add-on. michael@0: for (let addon of response.addons) { michael@0: if (!addon.debuggable) { michael@0: continue; michael@0: } michael@0: buildAddonLink(addon, parent); michael@0: } michael@0: } michael@0: else { michael@0: // Hide the section when there are no add-ons michael@0: parent.previousElementSibling.remove(); michael@0: parent.remove(); michael@0: } michael@0: michael@0: deferred = promise.defer(); michael@0: gClient.listTabs(deferred.resolve); michael@0: response = yield deferred.promise; michael@0: michael@0: parent = document.getElementById("tabActors"); michael@0: michael@0: // Add Global Process debugging... michael@0: let globals = JSON.parse(JSON.stringify(response)); michael@0: delete globals.tabs; michael@0: delete globals.selected; michael@0: // ...only if there are appropriate actors (a 'from' property will always michael@0: // be there). michael@0: michael@0: // Add one entry for each open tab. michael@0: for (let i = 0; i < response.tabs.length; i++) { michael@0: buildTabLink(response.tabs[i], parent, i == response.selected); michael@0: } michael@0: michael@0: let gParent = document.getElementById("globalActors"); michael@0: michael@0: // Build the Remote Process button michael@0: if (Object.keys(globals).length > 1) { michael@0: let a = document.createElement("a"); michael@0: a.onclick = function() { michael@0: openToolbox(globals, true); michael@0: michael@0: } michael@0: a.title = a.textContent = window.l10n.GetStringFromName("mainProcess"); michael@0: a.className = "remote-process"; michael@0: a.href = "#"; michael@0: gParent.appendChild(a); michael@0: } michael@0: // Move the selected tab on top michael@0: let selectedLink = parent.querySelector("a.selected"); michael@0: if (selectedLink) { michael@0: parent.insertBefore(selectedLink, parent.firstChild); michael@0: } michael@0: michael@0: document.body.classList.remove("connecting"); michael@0: document.body.classList.add("actors-mode"); michael@0: michael@0: // Ensure the first link is focused michael@0: let firstLink = parent.querySelector("a:first-of-type"); michael@0: if (firstLink) { michael@0: firstLink.focus(); michael@0: } michael@0: }); michael@0: michael@0: /** michael@0: * Build one button for an add-on actor. michael@0: */ michael@0: function buildAddonLink(addon, parent) { michael@0: let a = document.createElement("a"); michael@0: a.onclick = function() { michael@0: openToolbox({ addonActor: addon.actor, title: addon.name }, true, "jsdebugger"); michael@0: } michael@0: michael@0: a.textContent = addon.name; michael@0: a.title = addon.id; michael@0: a.href = "#"; michael@0: michael@0: parent.appendChild(a); michael@0: } michael@0: michael@0: /** michael@0: * Build one button for a tab actor. michael@0: */ michael@0: function buildTabLink(tab, parent, selected) { michael@0: let a = document.createElement("a"); michael@0: a.onclick = function() { michael@0: openToolbox(tab); michael@0: } michael@0: michael@0: a.textContent = tab.title; michael@0: a.title = tab.url; michael@0: if (!a.textContent) { michael@0: a.textContent = tab.url; michael@0: } michael@0: a.href = "#"; michael@0: michael@0: if (selected) { michael@0: a.classList.add("selected"); michael@0: } michael@0: michael@0: parent.appendChild(a); michael@0: } michael@0: michael@0: /** michael@0: * An error occured. Let's show it and return to the first screen. michael@0: */ michael@0: function showError(type) { michael@0: document.body.className = "error"; michael@0: let activeError = document.querySelector(".error-message.active"); michael@0: if (activeError) { michael@0: activeError.classList.remove("active"); michael@0: } michael@0: activeError = document.querySelector(".error-" + type); michael@0: if (activeError) { michael@0: activeError.classList.add("active"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Connection timeout. michael@0: */ michael@0: function handleConnectionTimeout() { michael@0: showError("timeout"); michael@0: } michael@0: michael@0: /** michael@0: * The user clicked on one of the buttons. michael@0: * Opens the toolbox. michael@0: */ michael@0: function openToolbox(form, chrome=false, tool="webconsole") { michael@0: let options = { michael@0: form: form, michael@0: client: gClient, michael@0: chrome: chrome michael@0: }; michael@0: devtools.TargetFactory.forRemoteTab(options).then((target) => { michael@0: let hostType = devtools.Toolbox.HostType.WINDOW; michael@0: gDevTools.showToolbox(target, tool, hostType).then((toolbox) => { michael@0: toolbox.once("destroyed", function() { michael@0: gClient.close(); michael@0: }); michael@0: }); michael@0: window.close(); michael@0: }); michael@0: }