diff options
Diffstat (limited to 'devtools/client/framework/connect')
-rw-r--r-- | devtools/client/framework/connect/connect.css | 112 | ||||
-rw-r--r-- | devtools/client/framework/connect/connect.js | 236 | ||||
-rw-r--r-- | devtools/client/framework/connect/connect.xhtml | 52 |
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> |