summaryrefslogtreecommitdiffstats
path: root/devtools/client/framework/connect
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/framework/connect')
-rw-r--r--devtools/client/framework/connect/connect.css112
-rw-r--r--devtools/client/framework/connect/connect.js236
-rw-r--r--devtools/client/framework/connect/connect.xhtml52
3 files changed, 400 insertions, 0 deletions
diff --git a/devtools/client/framework/connect/connect.css b/devtools/client/framework/connect/connect.css
new file mode 100644
index 000000000..23959b93b
--- /dev/null
+++ b/devtools/client/framework/connect/connect.css
@@ -0,0 +1,112 @@
+:root {
+ font: caption;
+}
+
+html {
+ background-color: #111;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ color: white;
+ max-width: 600px;
+ margin: 30px auto 0;
+ box-shadow: 0 2px 3px black;
+ background-color: #3C3E40;
+}
+
+h1 {
+ margin: 0;
+ padding: 20px;
+ background-color: rgba(0,0,0,0.12);
+ background-image: radial-gradient(ellipse farthest-corner at center top , rgb(159, 223, 255), rgba(101, 203, 255, 0.3)), radial-gradient(ellipse farthest-side at center top , rgba(101, 203, 255, 0.4), rgba(101, 203, 255, 0));
+ background-size: 100% 2px, 100% 5px;
+ background-repeat: no-repeat;
+ border-bottom: 1px solid rgba(0,0,0,0.1);
+}
+
+form {
+ display: inline-block;
+}
+
+label {
+ display: block;
+ margin: 10px;
+}
+
+label > span {
+ display: inline-block;
+ min-width: 150px;
+ text-align: right;
+ margin-right: 10px;
+}
+
+#submit {
+ float: right;
+}
+
+input:invalid {
+ box-shadow: 0 0 2px 2px #F06;
+}
+
+section {
+ min-height: 160px;
+ margin: 60px 20px;
+ display: none; /* By default, hidden */
+}
+
+.error-message {
+ color: red;
+}
+
+.error-message:not(.active) {
+ display: none;
+}
+
+body:not(.actors-mode):not(.connecting) > #connection-form {
+ display: block;
+}
+
+body.actors-mode > #actors-list {
+ display: block;
+}
+
+body.connecting > #connecting {
+ display: block;
+}
+
+#connecting {
+ text-align: center;
+}
+
+#connecting > p > img {
+ vertical-align: top;
+}
+
+.actors {
+ padding-left: 0;
+}
+
+.actors > a {
+ display: block;
+ margin: 5px;
+ padding: 5px;
+ color: white;
+}
+
+.remote-process {
+ font-style: italic;
+ opacity: 0.8;
+}
+
+footer {
+ padding: 10px;
+ background-color: rgba(0,0,0,0.12);
+ border-top: 1px solid rgba(0,0,0,0.1);
+ font-size: small;
+}
+
+footer > a,
+footer > a:visited {
+ color: white;
+}
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));
+}
diff --git a/devtools/client/framework/connect/connect.xhtml b/devtools/client/framework/connect/connect.xhtml
new file mode 100644
index 000000000..e8f8818f6
--- /dev/null
+++ b/devtools/client/framework/connect/connect.xhtml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+
+<!DOCTYPE html [
+<!ENTITY % connectionDTD SYSTEM "chrome://devtools/locale/connection-screen.dtd" >
+ %connectionDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <head>
+ <title>&title;</title>
+ <link rel="stylesheet" href="chrome://devtools/skin/dark-theme.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/framework/connect/connect.css" type="text/css"/>
+ <script type="application/javascript;version=1.8" src="connect.js"></script>
+ </head>
+ <body>
+ <h1>&header;</h1>
+ <section id="connection-form">
+ <form validate="validate" action="#">
+ <label>
+ <span>&host;</span>
+ <input required="required" class="devtools-textinput" id="host" type="text"></input>
+ </label>
+ <label>
+ <span>&port;</span>
+ <input required="required" class="devtools-textinput" id="port" type="number" pattern="\d+"></input>
+ </label>
+ <label>
+ <input class="devtools-toolbarbutton" id="submit" standalone="true" type="submit" value="&connect;"></input>
+ </label>
+ </form>
+ <p class="error-message error-timeout">&errorTimeout;</p>
+ <p class="error-message error-refused">&errorRefused;</p>
+ <p class="error-message error-unexpected">&errorUnexpected;</p>
+ </section>
+ <section id="actors-list">
+ <p>&availableTabs;</p>
+ <ul class="actors" id="tabActors"></ul>
+ <p>&availableAddons;</p>
+ <ul class="actors" id="addonActors"></ul>
+ <p>&availableProcesses;</p>
+ <ul class="actors" id="globalActors"></ul>
+ </section>
+ <section id="connecting">
+ <p class="devtools-throbber">&connecting;</p>
+ </section>
+ <footer>&remoteHelp;<a target='_' href='https://developer.mozilla.org/docs/Tools/Remote_Debugging'>&remoteDocumentation;</a>&remoteHelpSuffix;</footer>
+ </body>
+</html>