diff options
Diffstat (limited to 'toolkit/devtools/framework/connect/connect.js')
-rw-r--r-- | toolkit/devtools/framework/connect/connect.js | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/toolkit/devtools/framework/connect/connect.js b/toolkit/devtools/framework/connect/connect.js new file mode 100644 index 000000000..c4cd31759 --- /dev/null +++ b/toolkit/devtools/framework/connect/connect.js @@ -0,0 +1,239 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const Cu = Components.utils; +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); +let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); +let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); +let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); + +let gClient; +let gConnectionTimeout; + +XPCOMUtils.defineLazyGetter(window, 'l10n', function () { + return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties'); +}); + +/** + * Once DOM is ready, we prefil the host/port inputs with + * pref-stored values. + */ +window.addEventListener("DOMContentLoaded", function onDOMReady() { + window.removeEventListener("DOMContentLoaded", onDOMReady, true); + let host = Services.prefs.getCharPref("devtools.debugger.remote-host"); + let port = Services.prefs.getIntPref("devtools.debugger.remote-port"); + + if (host) { + document.getElementById("host").value = host; + } + + if (port) { + document.getElementById("port").value = port; + } + + let form = document.querySelector("#connection-form form"); + form.addEventListener("submit", function() { + window.submit().catch(e => { + Cu.reportError(e); + // Bug 921850: catch rare exception from DebuggerClient.socketConnect + showError("unexpected"); + }); + }); +}, true); + +/** + * Called when the "connect" button is clicked. + */ +let submit = Task.async(function*() { + // Show the "connecting" screen + document.body.classList.add("connecting"); + + let host = document.getElementById("host").value; + let port = document.getElementById("port").value; + + // Save the host/port values + try { + Services.prefs.setCharPref("devtools.debugger.remote-host", host); + Services.prefs.setIntPref("devtools.debugger.remote-port", port); + } catch(e) { + // Fails in e10s mode, but not a critical feature. + } + + // Initiate the connection + let transport = yield DebuggerClient.socketConnect({ host, port }); + gClient = new DebuggerClient(transport); + let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout"); + gConnectionTimeout = setTimeout(handleConnectionTimeout, delay); + let response = yield clientConnect(); + yield onConnectionReady(...response); +}); + +function clientConnect() { + let deferred = promise.defer(); + gClient.connect((...args) => deferred.resolve(args)); + return deferred.promise; +} + +/** + * Connection is ready. List actors and build buttons. + */ +let onConnectionReady = Task.async(function*(aType, aTraits) { + clearTimeout(gConnectionTimeout); + + let deferred = promise.defer(); + gClient.listAddons(deferred.resolve); + let response = yield deferred.promise; + + let parent = document.getElementById("addonActors") + if (!response.error && response.addons.length > 0) { + // Add one entry for each add-on. + for (let addon of response.addons) { + if (!addon.debuggable) { + continue; + } + buildAddonLink(addon, parent); + } + } + else { + // Hide the section when there are no add-ons + parent.previousElementSibling.remove(); + parent.remove(); + } + + deferred = promise.defer(); + gClient.listTabs(deferred.resolve); + response = yield deferred.promise; + + parent = document.getElementById("tabActors"); + + // Add Global Process debugging... + let globals = Cu.cloneInto(response, {}); + delete globals.tabs; + delete globals.selected; + // ...only if there are appropriate actors (a 'from' property will always + // be there). + + // Add one entry for each open tab. + for (let i = 0; i < response.tabs.length; i++) { + buildTabLink(response.tabs[i], parent, i == response.selected); + } + + let gParent = document.getElementById("globalActors"); + + // Build the Remote Process button + if (Object.keys(globals).length > 1) { + let a = document.createElement("a"); + a.onclick = function() { + openToolbox(globals, true); + + } + a.title = a.textContent = window.l10n.GetStringFromName("mainProcess"); + a.className = "remote-process"; + a.href = "#"; + gParent.appendChild(a); + } + // Move the selected tab on top + let selectedLink = parent.querySelector("a.selected"); + if (selectedLink) { + parent.insertBefore(selectedLink, parent.firstChild); + } + + document.body.classList.remove("connecting"); + document.body.classList.add("actors-mode"); + + // Ensure the first link is focused + let firstLink = parent.querySelector("a:first-of-type"); + if (firstLink) { + firstLink.focus(); + } +}); + +/** + * Build one button for an add-on actor. + */ +function buildAddonLink(addon, parent) { + let a = document.createElement("a"); + a.onclick = function() { + openToolbox(addon, true, "jsdebugger"); + } + + a.textContent = addon.name; + a.title = addon.id; + a.href = "#"; + + parent.appendChild(a); +} + +/** + * Build one button for a tab actor. + */ +function buildTabLink(tab, parent, selected) { + let a = document.createElement("a"); + a.onclick = function() { + openToolbox(tab); + } + + a.textContent = tab.title; + a.title = tab.url; + if (!a.textContent) { + a.textContent = tab.url; + } + a.href = "#"; + + if (selected) { + a.classList.add("selected"); + } + + parent.appendChild(a); +} + +/** + * An error occured. Let's show it and return to the first screen. + */ +function showError(type) { + document.body.className = "error"; + let activeError = document.querySelector(".error-message.active"); + if (activeError) { + activeError.classList.remove("active"); + } + activeError = document.querySelector(".error-" + type); + if (activeError) { + activeError.classList.add("active"); + } +} + +/** + * Connection timeout. + */ +function handleConnectionTimeout() { + showError("timeout"); +} + +/** + * The user clicked on one of the buttons. + * Opens the toolbox. + */ +function openToolbox(form, chrome=false, tool="webconsole") { + let options = { + form: form, + client: gClient, + chrome: chrome + }; + devtools.TargetFactory.forRemoteTab(options).then((target) => { + let hostType = devtools.Toolbox.HostType.WINDOW; + gDevTools.showToolbox(target, tool, hostType).then((toolbox) => { + toolbox.once("destroyed", function() { + gClient.close(); + }); + }); + window.close(); + }); +} |