summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkaznacheev@chromium.org <kaznacheev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-05 15:42:38 +0000
committerkaznacheev@chromium.org <kaznacheev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-05 15:42:38 +0000
commit23c6626aa48f8f04f4b4193951d41b901eee0001 (patch)
treeec74cfe42dbbe8cc3b9c8050dd53e0e09d0f9240
parentbd7e8b3aa836266abc1996b06e16fcf4fa2a9459 (diff)
downloadchromium_src-23c6626aa48f8f04f4b4193951d41b901eee0001.zip
chromium_src-23c6626aa48f8f04f4b4193951d41b901eee0001.tar.gz
chromium_src-23c6626aa48f8f04f4b4193951d41b901eee0001.tar.bz2
DevTools: Implement port forwarding UI as a part of chrome://inspect
BUG=None Review URL: https://chromiumcodereview.appspot.com/21222002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215619 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/resources/inspect/inspect.css118
-rw-r--r--chrome/browser/resources/inspect/inspect.html19
-rw-r--r--chrome/browser/resources/inspect/inspect.js232
-rw-r--r--chrome/browser/ui/webui/inspect_ui.cc82
-rw-r--r--chrome/browser/ui/webui/inspect_ui.h9
5 files changed, 449 insertions, 11 deletions
diff --git a/chrome/browser/resources/inspect/inspect.css b/chrome/browser/resources/inspect/inspect.css
index 1d5bc7d..6316a17 100644
--- a/chrome/browser/resources/inspect/inspect.css
+++ b/chrome/browser/resources/inspect/inspect.css
@@ -103,3 +103,121 @@ a {
color: rgb(17, 85, 204);
text-decoration: none;
}
+
+#port-forwarding-settings {
+ position: absolute;
+ right: 20px;
+ top: 15px;
+}
+
+#port-forwarding-overlay {
+ -webkit-box-align: center;
+ -webkit-box-pack: center;
+ background-color: rgba(255, 255, 255, 0.75);
+ bottom: 0;
+ display: -webkit-box;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+#port-forwarding-overlay:not(.open) {
+ display: none;
+}
+
+#port-forwarding-config {
+ -webkit-border-radius: 3px;
+ background: white;
+ box-shadow: 0 4px 23px 5px rgba(0, 0, 0, 0.2), 0 2px 6px rgba(0,0,0,0.15);
+ color: #333;
+ padding: 17px;
+ position: relative;
+}
+
+.close-button {
+ background-image: url('chrome://theme/IDR_CLOSE_DIALOG');
+ height: 14px;
+ width: 14px;
+}
+
+.close-button:active {
+ background-image: url('chrome://theme/IDR_CLOSE_DIALOG_P');
+}
+
+.close-button:hover {
+ background-image: url('chrome://theme/IDR_CLOSE_DIALOG_H');
+}
+
+#port-forwarding-config > .close-button {
+ position: absolute;
+ right: 7px;
+ top: 7px;
+}
+
+#port-forwarding-config-title {
+ font-size: 130%;
+}
+
+#port-forwarding-config-list {
+ border: 1px solid #eee;
+ height: 180px;
+ margin-bottom: 10px;
+ margin-top: 10px;
+ overflow-x: hidden;
+}
+
+.port-forwarding-pair {
+ -webkit-flex-direction: row;
+ display: -webkit-flex;
+}
+
+.port-forwarding-pair:hover {
+ background-color: #eee;
+}
+
+.port-forwarding-pair.selected,
+.port-forwarding-pair.selected:hover {
+ background-color: #ccc;
+}
+
+.port-forwarding-pair input {
+ border: 1px solid transparent;
+ line-height: 20px;
+ margin: 4px;
+ padding: 0 3px;
+}
+
+.port-forwarding-pair.fresh:not(.selected) input {
+ border-color: #eee;
+}
+
+.port-forwarding-pair input.port {
+ width: 4em;
+}
+
+.port-forwarding-pair input.location {
+ -webkit-flex: 1;
+}
+
+.port-forwarding-pair:not(.empty) input.invalid {
+ background-color: rgb(255, 200, 200);
+}
+
+.port-forwarding-pair .close-button {
+ margin: 8px 8px;
+}
+
+.port-forwarding-pair.fresh .close-button,
+.port-forwarding-pair:not(.selected):not(:hover) .close-button:not(:hover) {
+ background-image: none;
+ pointer-events: none;
+}
+
+.port-forwarding-pair:not(.selected) .close-button:not(:hover) {
+ opacity: 0.5;
+}
+
+#port-forwarding-config-buttons > button {
+ float: right;
+}
diff --git a/chrome/browser/resources/inspect/inspect.html b/chrome/browser/resources/inspect/inspect.html
index f9e9d33..861fcdf 100644
--- a/chrome/browser/resources/inspect/inspect.html
+++ b/chrome/browser/resources/inspect/inspect.html
@@ -23,6 +23,14 @@ found in the LICENSE file.
<div id="content">
<div id="devices-tab">
<div class="content-header">Devices</div>
+ <div id="port-forwarding-settings">
+ <label for="port-forwarding-enable">
+ <input id="port-forwarding-enable" type="checkbox" disabled/>
+ <span>Enable port forwarding</span>
+ </label>
+ <button id="port-forwarding-config-open" disabled>
+ Configure port forwarding</button>
+ </div>
<div id="devices"></div>
</div>
<div id="pages-tab">
@@ -47,5 +55,16 @@ found in the LICENSE file.
</div>
</div>
</div>
+<div id="port-forwarding-overlay">
+ <div id="port-forwarding-config">
+ <div id="port-forwarding-config-close" class="close-button"></div>
+ <div id="port-forwarding-config-title">Port forwarding settings</div>
+ <div id="port-forwarding-config-list">
+ </div>
+ <div id="port-forwarding-config-buttons">
+ <button id="port-forwarding-config-done">Done</button>
+ </div>
+ </div>
+</div>
</body>
</html>
diff --git a/chrome/browser/resources/inspect/inspect.js b/chrome/browser/resources/inspect/inspect.js
index 12bff1c..856744b 100644
--- a/chrome/browser/resources/inspect/inspect.js
+++ b/chrome/browser/resources/inspect/inspect.js
@@ -31,6 +31,7 @@ function onload() {
}
var selectedTabName = window.location.hash.slice(1) || 'devices';
selectTab(selectedTabName + '-tab');
+ initPortForwarding();
chrome.send('init-ui');
}
@@ -206,4 +207,235 @@ function createTerminateElement(data) {
return link;
}
+
+function initPortForwarding() {
+ $('port-forwarding-enable').addEventListener('change', enablePortForwarding);
+
+ $('port-forwarding-config-open').addEventListener(
+ 'click', openPortForwardingConfig);
+ $('port-forwarding-config-close').addEventListener(
+ 'click', closePortForwardingConfig);
+ $('port-forwarding-config-done').addEventListener(
+ 'click', commitPortForwardingConfig);
+}
+
+function enablePortForwarding(event) {
+ chrome.send('set-port-forwarding-enabled', [event.target.checked]);
+}
+
+function handleKey(event) {
+ switch (event.keyCode) {
+ case 13: // Enter
+ if (event.target.nodeName == 'INPUT') {
+ var line = event.target.parentNode;
+ if (!line.classList.contains('fresh') ||
+ line.classList.contains('empty'))
+ commitPortForwardingConfig();
+ else
+ commitFreshLineIfValid(true /* select new line */);
+ } else {
+ commitPortForwardingConfig();
+ }
+ break;
+
+ case 27:
+ closePortForwardingConfig();
+ break;
+ }
+}
+
+function openPortForwardingConfig() {
+ loadPortForwardingConfig(window.portForwardingConfig);
+
+ $('port-forwarding-overlay').classList.add('open');
+ document.addEventListener('keyup', handleKey);
+
+ var freshPort = document.querySelector('.fresh .port');
+ if (freshPort)
+ freshPort.focus();
+ else
+ $('port-forwarding-config-done').focus();
+}
+
+function closePortForwardingConfig() {
+ $('port-forwarding-overlay').classList.remove('open');
+ document.removeEventListener('keyup', handleKey);
+}
+
+function loadPortForwardingConfig(config) {
+ var list = $('port-forwarding-config-list');
+ list.textContent = '';
+ for (var port in config)
+ list.appendChild(createConfigLine(port, config[port]));
+ list.appendChild(createEmptyConfigLine());
+}
+
+function commitPortForwardingConfig() {
+ if (document.querySelector(
+ '.port-forwarding-pair:not(.fresh) input.invalid'))
+ return;
+
+ if (document.querySelector(
+ '.port-forwarding-pair.fresh:not(.empty) input.invalid'))
+ return;
+
+ closePortForwardingConfig();
+ commitFreshLineIfValid();
+ var lines = document.querySelectorAll('.port-forwarding-pair');
+ var config = {};
+ for (var i = 0; i != lines.length; i++) {
+ var line = lines[i];
+ var portInput = line.querySelector('.port:not(.invalid)');
+ var locationInput = line.querySelector('.location:not(.invalid)');
+ if (portInput && locationInput)
+ config[portInput.value] = locationInput.value;
+ }
+ chrome.send('set-port-forwarding-config', [config]);
+}
+
+function updatePortForwardingEnabled(enabled) {
+ var checkbox = $('port-forwarding-enable');
+ checkbox.checked = !!enabled;
+ checkbox.disabled = false;
+}
+
+function updatePortForwardingConfig(config) {
+ window.portForwardingConfig = config;
+ $('port-forwarding-config-open').disabled = !config;
+}
+
+function createConfigLine(port, location) {
+ var line = document.createElement('div');
+ line.className = 'port-forwarding-pair';
+
+ var portInput = createConfigField(port, 'port', 'Port', validatePort);
+ line.appendChild(portInput);
+
+ var locationInput = createConfigField(
+ location, 'location', 'IP address and port', validateLocation);
+ line.appendChild(locationInput);
+ locationInput.addEventListener('keydown', function(e) {
+ if (e.keyIdentifier == 'U+0009' && // Tab
+ !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey &&
+ line.classList.contains('fresh') &&
+ !line.classList.contains('empty')) {
+ // Tabbing forward on the fresh line, try create a new empty one.
+ commitFreshLineIfValid(true);
+ e.preventDefault();
+ }
+ });
+
+ var lineDelete = document.createElement('div');
+ lineDelete.className = 'close-button';
+ lineDelete.addEventListener('click', function() {
+ var newSelection = line.nextElementSibling;
+ line.parentNode.removeChild(line);
+ selectLine(newSelection);
+ });
+ line.appendChild(lineDelete);
+
+ line.addEventListener('click', selectLine.bind(null, line));
+ line.addEventListener('focus', selectLine.bind(null, line));
+
+ checkEmptyLine(line);
+
+ return line;
+}
+
+function validatePort(input) {
+ var match = input.value.match(/^(\d+)$/);
+ if (!match)
+ return false;
+ var port = parseInt(match[1]);
+ if (port < 5000 || 10000 < port)
+ return false;
+
+ var inputs = document.querySelectorAll('input.port:not(.invalid)');
+ for (var i = 0; i != inputs.length; ++i) {
+ if (inputs[i] == input)
+ break;
+ if (parseInt(inputs[i].value) == port)
+ return false;
+ }
+ return true;
+}
+
+function validateLocation(input) {
+ var match = input.value.match(/^([a-zA-Z0-9\.]+):(\d+)$/);
+ if (!match)
+ return false;
+ var port = parseInt(match[2]);
+ return port <= 10000;
+}
+
+function createEmptyConfigLine() {
+ var line = createConfigLine('', '');
+ line.classList.add('fresh');
+ return line;
+}
+
+function createConfigField(value, className, hint, validate) {
+ var input = document.createElement('input');
+ input.className = className;
+ input.type = 'text';
+ input.placeholder = hint;
+ input.value = value;
+
+ function checkInput() {
+ if (validate(input))
+ input.classList.remove('invalid');
+ else
+ input.classList.add('invalid');
+ if (input.parentNode)
+ checkEmptyLine(input.parentNode);
+ }
+ checkInput();
+
+ input.addEventListener('keyup', checkInput);
+ input.addEventListener('focus', function() {
+ selectLine(input.parentNode);
+ });
+
+ return input;
+}
+
+function checkEmptyLine(line) {
+ var inputs = line.querySelectorAll('input');
+ var empty = true;
+ for (var i = 0; i != inputs.length; i++) {
+ if (inputs[i].value != '')
+ empty = false;
+ }
+ if (empty)
+ line.classList.add('empty');
+ else
+ line.classList.remove('empty');
+}
+
+function selectLine(line) {
+ if (line.classList.contains('selected'))
+ return;
+ unselectLine();
+ line.classList.add('selected');
+}
+
+function unselectLine() {
+ var line = document.querySelector('.port-forwarding-pair.selected');
+ if (!line)
+ return;
+ line.classList.remove('selected');
+ commitFreshLineIfValid();
+}
+
+function commitFreshLineIfValid(opt_selectNew) {
+ var line = document.querySelector('.port-forwarding-pair.fresh');
+ if (line.querySelector('.invalid'))
+ return;
+ line.classList.remove('fresh');
+ var freshLine = createEmptyConfigLine();
+ line.parentNode.appendChild(freshLine);
+ if (opt_selectNew)
+ freshLine.querySelector('.port').focus();
+}
+
document.addEventListener('DOMContentLoaded', onload);
diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc
index 238dc63..d75405c 100644
--- a/chrome/browser/ui/webui/inspect_ui.cc
+++ b/chrome/browser/ui/webui/inspect_ui.cc
@@ -18,6 +18,7 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
+#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
@@ -72,6 +73,9 @@ static const char kAdbTargetType[] = "adb_page";
static const char kInitUICommand[] = "init-ui";
static const char kInspectCommand[] = "inspect";
static const char kTerminateCommand[] = "terminate";
+static const char kPortForwardingEnabledCommand[] =
+ "set-port-forwarding-enabled";
+static const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
static const char kTargetTypeField[] = "type";
static const char kAttachedField[] = "attached";
@@ -173,6 +177,8 @@ class InspectMessageHandler : public WebUIMessageHandler {
void HandleInitUICommand(const ListValue* args);
void HandleInspectCommand(const ListValue* args);
void HandleTerminateCommand(const ListValue* args);
+ void HandlePortForwardingEnabledCommand(const ListValue* args);
+ void HandlePortForwardingConfigCommand(const ListValue* args);
bool GetProcessAndRouteId(const ListValue* args,
int* process_id,
@@ -193,6 +199,12 @@ void InspectMessageHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(kTerminateCommand,
base::Bind(&InspectMessageHandler::HandleTerminateCommand,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
+ base::Bind(&InspectMessageHandler::HandlePortForwardingEnabledCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
+ base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
+ base::Unretained(this)));
}
void InspectMessageHandler::HandleInitUICommand(const ListValue*) {
@@ -258,6 +270,30 @@ bool InspectMessageHandler::GetProcessAndRouteId(const ListValue* args,
return false;
}
+void InspectMessageHandler::HandlePortForwardingEnabledCommand(
+ const ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return;
+
+ bool enabled;
+ if (args->GetSize() == 1 && args->GetBoolean(0, &enabled)) {
+ profile->GetPrefs()->SetBoolean(
+ prefs::kDevToolsPortForwardingEnabled, enabled);
+ }
+}
+
+void InspectMessageHandler::HandlePortForwardingConfigCommand(
+ const ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return;
+
+ const DictionaryValue* dict_src;
+ if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
+ profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
+}
+
} // namespace
class InspectUI::WorkerCreationDestructionListener
@@ -365,6 +401,8 @@ InspectUI::~InspectUI() {
void InspectUI::InitUI() {
StartListeningNotifications();
PopulateLists();
+ UpdatePortForwardingEnabled();
+ UpdatePortForwardingConfig();
observer_->InitUI();
}
@@ -416,15 +454,23 @@ void InspectUI::StartListeningNotifications() {
if (adb_bridge)
adb_bridge->AddListener(this);
- registrar_.Add(this,
- content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
- content::NotificationService::AllSources());
- registrar_.Add(this,
- content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
- content::NotificationService::AllSources());
- registrar_.Add(this,
- content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
- content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::NotificationService::AllSources());
+
+ pref_change_registrar_.Init(profile->GetPrefs());
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
+ base::Bind(&InspectUI::UpdatePortForwardingEnabled,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
+ base::Bind(&InspectUI::UpdatePortForwardingConfig,
+ base::Unretained(this)));
}
void InspectUI::StopListeningNotifications()
@@ -438,7 +484,8 @@ void InspectUI::StopListeningNotifications()
adb_bridge->RemoveListener(this);
observer_->InspectUIDestroyed();
observer_ = NULL;
- registrar_.RemoveAll();
+ notification_registrar_.RemoveAll();
+ pref_change_registrar_.RemoveAll();
}
content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
@@ -489,3 +536,18 @@ void InspectUI::RemoteDevicesChanged(
}
web_ui()->CallJavascriptFunction("populateDeviceLists", device_list);
}
+
+void InspectUI::UpdatePortForwardingEnabled() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const base::Value* value = profile->GetPrefs()->FindPreference(
+ prefs::kDevToolsPortForwardingEnabled)->GetValue();
+ web_ui()->CallJavascriptFunction("updatePortForwardingEnabled", *value);
+
+}
+
+void InspectUI::UpdatePortForwardingConfig() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const base::Value* value = profile->GetPrefs()->FindPreference(
+ prefs::kDevToolsPortForwardingConfig)->GetValue();
+ web_ui()->CallJavascriptFunction("updatePortForwardingConfig", *value);
+}
diff --git a/chrome/browser/ui/webui/inspect_ui.h b/chrome/browser/ui/webui/inspect_ui.h
index a0f7952..8f5b4d9 100644
--- a/chrome/browser/ui/webui/inspect_ui.h
+++ b/chrome/browser/ui/webui/inspect_ui.h
@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/prefs/pref_change_registrar.h"
#include "chrome/browser/devtools/devtools_adb_bridge.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
@@ -44,10 +45,16 @@ class InspectUI : public content::WebUIController,
virtual void RemoteDevicesChanged(
DevToolsAdbBridge::RemoteDevices* devices) OVERRIDE;
+ void UpdatePortForwardingEnabled();
+ void UpdatePortForwardingConfig();
+
scoped_refptr<WorkerCreationDestructionListener> observer_;
// A scoped container for notification registries.
- content::NotificationRegistrar registrar_;
+ content::NotificationRegistrar notification_registrar_;
+
+ // A scoped container for preference change registries.
+ PrefChangeRegistrar pref_change_registrar_;
typedef std::map<std::string, scoped_refptr<DevToolsAdbBridge::RemotePage> >
RemotePages;