summaryrefslogtreecommitdiffstats
path: root/devtools/client/framework/connect/connect.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/framework/connect/connect.js')
-rw-r--r--devtools/client/framework/connect/connect.js236
1 files changed, 236 insertions, 0 deletions
diff --git a/devtools/client/framework/connect/connect.js b/devtools/client/framework/connect/connect.js
new file mode 100644
index 000000000..d713231f9
--- /dev/null
+++ b/devtools/client/framework/connect/connect.js
@@ -0,0 +1,236 @@
+/* -*- 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";
+
+var Cu = Components.utils;
+var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
+var {gDevTools} = require("devtools/client/framework/devtools");
+var {TargetFactory} = require("devtools/client/framework/target");
+var {Toolbox} = require("devtools/client/framework/toolbox");
+var {DebuggerClient} = require("devtools/shared/client/main");
+var {Task} = require("devtools/shared/task");
+var {LocalizationHelper} = require("devtools/shared/l10n");
+var L10N = new LocalizationHelper("devtools/client/locales/connection-screen.properties");
+
+var gClient;
+var gConnectionTimeout;
+
+/**
+ * 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 => {
+ console.error(e);
+ // Bug 921850: catch rare exception from DebuggerClient.socketConnect
+ showError("unexpected");
+ });
+ });
+}, true);
+
+/**
+ * Called when the "connect" button is clicked.
+ */
+var 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 gClient.connect();
+ yield onConnectionReady(...response);
+});
+
+/**
+ * Connection is ready. List actors and build buttons.
+ */
+var onConnectionReady = Task.async(function* ([aType, aTraits]) {
+ clearTimeout(gConnectionTimeout);
+
+ let response = yield gClient.listAddons();
+
+ 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();
+ }
+
+ response = yield gClient.listTabs();
+
+ 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 = L10N.getStr("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
+ };
+ TargetFactory.forRemoteTab(options).then((target) => {
+ let hostType = 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));
+}