1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/framework/connect/connect.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,231 @@ 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 +"use strict"; 1.11 + 1.12 +const Cu = Components.utils; 1.13 +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); 1.14 +Cu.import("resource://gre/modules/Services.jsm"); 1.15 +Cu.import("resource://gre/modules/Task.jsm"); 1.16 +Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); 1.17 +let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); 1.18 +let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); 1.19 +let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); 1.20 + 1.21 +let gClient; 1.22 +let gConnectionTimeout; 1.23 + 1.24 +XPCOMUtils.defineLazyGetter(window, 'l10n', function () { 1.25 + return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties'); 1.26 +}); 1.27 + 1.28 +/** 1.29 + * Once DOM is ready, we prefil the host/port inputs with 1.30 + * pref-stored values. 1.31 + */ 1.32 +window.addEventListener("DOMContentLoaded", function onDOMReady() { 1.33 + window.removeEventListener("DOMContentLoaded", onDOMReady, true); 1.34 + let host = Services.prefs.getCharPref("devtools.debugger.remote-host"); 1.35 + let port = Services.prefs.getIntPref("devtools.debugger.remote-port"); 1.36 + 1.37 + if (host) { 1.38 + document.getElementById("host").value = host; 1.39 + } 1.40 + 1.41 + if (port) { 1.42 + document.getElementById("port").value = port; 1.43 + } 1.44 + 1.45 + let form = document.querySelector("#connection-form form"); 1.46 + form.addEventListener("submit", function() { 1.47 + window.submit(); 1.48 + }); 1.49 +}, true); 1.50 + 1.51 +/** 1.52 + * Called when the "connect" button is clicked. 1.53 + */ 1.54 +function submit() { 1.55 + // Show the "connecting" screen 1.56 + document.body.classList.add("connecting"); 1.57 + 1.58 + // Save the host/port values 1.59 + let host = document.getElementById("host").value; 1.60 + Services.prefs.setCharPref("devtools.debugger.remote-host", host); 1.61 + 1.62 + let port = document.getElementById("port").value; 1.63 + Services.prefs.setIntPref("devtools.debugger.remote-port", port); 1.64 + 1.65 + // Initiate the connection 1.66 + let transport; 1.67 + try { 1.68 + transport = debuggerSocketConnect(host, port); 1.69 + } catch(e) { 1.70 + // Bug 921850: catch rare exception from debuggerSocketConnect 1.71 + showError("unexpected"); 1.72 + return; 1.73 + } 1.74 + gClient = new DebuggerClient(transport); 1.75 + let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout"); 1.76 + gConnectionTimeout = setTimeout(handleConnectionTimeout, delay); 1.77 + gClient.connect(onConnectionReady); 1.78 +} 1.79 + 1.80 +/** 1.81 + * Connection is ready. List actors and build buttons. 1.82 + */ 1.83 +let onConnectionReady = Task.async(function*(aType, aTraits) { 1.84 + clearTimeout(gConnectionTimeout); 1.85 + 1.86 + let deferred = promise.defer(); 1.87 + gClient.listAddons(deferred.resolve); 1.88 + let response = yield deferred.promise; 1.89 + 1.90 + let parent = document.getElementById("addonActors") 1.91 + if (!response.error && response.addons.length > 0) { 1.92 + // Add one entry for each add-on. 1.93 + for (let addon of response.addons) { 1.94 + if (!addon.debuggable) { 1.95 + continue; 1.96 + } 1.97 + buildAddonLink(addon, parent); 1.98 + } 1.99 + } 1.100 + else { 1.101 + // Hide the section when there are no add-ons 1.102 + parent.previousElementSibling.remove(); 1.103 + parent.remove(); 1.104 + } 1.105 + 1.106 + deferred = promise.defer(); 1.107 + gClient.listTabs(deferred.resolve); 1.108 + response = yield deferred.promise; 1.109 + 1.110 + parent = document.getElementById("tabActors"); 1.111 + 1.112 + // Add Global Process debugging... 1.113 + let globals = JSON.parse(JSON.stringify(response)); 1.114 + delete globals.tabs; 1.115 + delete globals.selected; 1.116 + // ...only if there are appropriate actors (a 'from' property will always 1.117 + // be there). 1.118 + 1.119 + // Add one entry for each open tab. 1.120 + for (let i = 0; i < response.tabs.length; i++) { 1.121 + buildTabLink(response.tabs[i], parent, i == response.selected); 1.122 + } 1.123 + 1.124 + let gParent = document.getElementById("globalActors"); 1.125 + 1.126 + // Build the Remote Process button 1.127 + if (Object.keys(globals).length > 1) { 1.128 + let a = document.createElement("a"); 1.129 + a.onclick = function() { 1.130 + openToolbox(globals, true); 1.131 + 1.132 + } 1.133 + a.title = a.textContent = window.l10n.GetStringFromName("mainProcess"); 1.134 + a.className = "remote-process"; 1.135 + a.href = "#"; 1.136 + gParent.appendChild(a); 1.137 + } 1.138 + // Move the selected tab on top 1.139 + let selectedLink = parent.querySelector("a.selected"); 1.140 + if (selectedLink) { 1.141 + parent.insertBefore(selectedLink, parent.firstChild); 1.142 + } 1.143 + 1.144 + document.body.classList.remove("connecting"); 1.145 + document.body.classList.add("actors-mode"); 1.146 + 1.147 + // Ensure the first link is focused 1.148 + let firstLink = parent.querySelector("a:first-of-type"); 1.149 + if (firstLink) { 1.150 + firstLink.focus(); 1.151 + } 1.152 +}); 1.153 + 1.154 +/** 1.155 + * Build one button for an add-on actor. 1.156 + */ 1.157 +function buildAddonLink(addon, parent) { 1.158 + let a = document.createElement("a"); 1.159 + a.onclick = function() { 1.160 + openToolbox({ addonActor: addon.actor, title: addon.name }, true, "jsdebugger"); 1.161 + } 1.162 + 1.163 + a.textContent = addon.name; 1.164 + a.title = addon.id; 1.165 + a.href = "#"; 1.166 + 1.167 + parent.appendChild(a); 1.168 +} 1.169 + 1.170 +/** 1.171 + * Build one button for a tab actor. 1.172 + */ 1.173 +function buildTabLink(tab, parent, selected) { 1.174 + let a = document.createElement("a"); 1.175 + a.onclick = function() { 1.176 + openToolbox(tab); 1.177 + } 1.178 + 1.179 + a.textContent = tab.title; 1.180 + a.title = tab.url; 1.181 + if (!a.textContent) { 1.182 + a.textContent = tab.url; 1.183 + } 1.184 + a.href = "#"; 1.185 + 1.186 + if (selected) { 1.187 + a.classList.add("selected"); 1.188 + } 1.189 + 1.190 + parent.appendChild(a); 1.191 +} 1.192 + 1.193 +/** 1.194 + * An error occured. Let's show it and return to the first screen. 1.195 + */ 1.196 +function showError(type) { 1.197 + document.body.className = "error"; 1.198 + let activeError = document.querySelector(".error-message.active"); 1.199 + if (activeError) { 1.200 + activeError.classList.remove("active"); 1.201 + } 1.202 + activeError = document.querySelector(".error-" + type); 1.203 + if (activeError) { 1.204 + activeError.classList.add("active"); 1.205 + } 1.206 +} 1.207 + 1.208 +/** 1.209 + * Connection timeout. 1.210 + */ 1.211 +function handleConnectionTimeout() { 1.212 + showError("timeout"); 1.213 +} 1.214 + 1.215 +/** 1.216 + * The user clicked on one of the buttons. 1.217 + * Opens the toolbox. 1.218 + */ 1.219 +function openToolbox(form, chrome=false, tool="webconsole") { 1.220 + let options = { 1.221 + form: form, 1.222 + client: gClient, 1.223 + chrome: chrome 1.224 + }; 1.225 + devtools.TargetFactory.forRemoteTab(options).then((target) => { 1.226 + let hostType = devtools.Toolbox.HostType.WINDOW; 1.227 + gDevTools.showToolbox(target, tool, hostType).then((toolbox) => { 1.228 + toolbox.once("destroyed", function() { 1.229 + gClient.close(); 1.230 + }); 1.231 + }); 1.232 + window.close(); 1.233 + }); 1.234 +}