Files
tubestation/browser/devtools/framework/connect/connect.js
2015-03-24 14:59:53 -05:00

249 lines
7.0 KiB
JavaScript

/* -*- 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 Fx<39, tab actors were used to be exposed on RootActor
// but in Fx>=39, chrome is debuggable via getProcess() and ChromeActor
if (globals.consoleActor || gClient.mainRoot.traits.allowChromeProcess) {
let a = document.createElement("a");
a.onclick = function() {
if (gClient.mainRoot.traits.allowChromeProcess) {
gClient.getProcess()
.then(aResponse => {
openToolbox(aResponse.form, true);
});
} else if (globals.consoleActor) {
openToolbox(globals, true, "webconsole", false);
}
}
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", false);
}
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", isTabActor) {
let options = {
form: form,
client: gClient,
chrome: chrome,
isTabActor: isTabActor
};
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();
});
}, console.error.bind(console));
window.close();
}, console.error.bind(console));
}