summaryrefslogtreecommitdiff
path: root/toolkit/devtools/framework/connect/connect.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/devtools/framework/connect/connect.js')
-rw-r--r--toolkit/devtools/framework/connect/connect.js239
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();
+ });
+}