diff options
author | kaznacheev@chromium.org <kaznacheev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 15:42:38 +0000 |
---|---|---|
committer | kaznacheev@chromium.org <kaznacheev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 15:42:38 +0000 |
commit | 23c6626aa48f8f04f4b4193951d41b901eee0001 (patch) | |
tree | ec74cfe42dbbe8cc3b9c8050dd53e0e09d0f9240 | |
parent | bd7e8b3aa836266abc1996b06e16fcf4fa2a9459 (diff) | |
download | chromium_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.css | 118 | ||||
-rw-r--r-- | chrome/browser/resources/inspect/inspect.html | 19 | ||||
-rw-r--r-- | chrome/browser/resources/inspect/inspect.js | 232 | ||||
-rw-r--r-- | chrome/browser/ui/webui/inspect_ui.cc | 82 | ||||
-rw-r--r-- | chrome/browser/ui/webui/inspect_ui.h | 9 |
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; |