blob: 8ce04b8d6dee0dac7bfdec2a4dbb00aa045e4227 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Torne (Richard Coles)58218062012-11-14 11:43:16 +00005function inspect(data) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01006 chrome.send('inspect', [data]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00007}
8
9function terminate(data) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010010 chrome.send('terminate', [data]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000011}
12
Ben Murdochbb1529c2013-08-08 10:24:53 +010013function reload(data) {
14 chrome.send('reload', [data]);
15}
16
17function open(browserId, url) {
18 chrome.send('open', [browserId, url]);
19}
20
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021function removeChildren(element_id) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010022 var element = $(element_id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000023 element.textContent = '';
24}
25
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000026function onload() {
Ben Murdochca12bfa2013-07-23 11:17:05 +010027 var tabContents = document.querySelectorAll('#content > div');
28 for (var i = 0; i != tabContents.length; i++) {
29 var tabContent = tabContents[i];
30 var tabName = tabContent.querySelector('.content-header').textContent;
31
32 var tabHeader = document.createElement('div');
33 tabHeader.className = 'tab-header';
34 var button = document.createElement('button');
35 button.textContent = tabName;
36 tabHeader.appendChild(button);
37 tabHeader.addEventListener('click', selectTab.bind(null, tabContent.id));
38 $('navigation').appendChild(tabHeader);
39 }
Ben Murdochbbcdd452013-07-25 10:06:34 +010040 var selectedTabName = window.location.hash.slice(1) || 'devices';
41 selectTab(selectedTabName + '-tab');
Ben Murdoch2385ea32013-08-06 11:01:04 +010042 initPortForwarding();
Ben Murdochbbcdd452013-07-25 10:06:34 +010043 chrome.send('init-ui');
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000044}
45
Ben Murdochca12bfa2013-07-23 11:17:05 +010046function selectTab(id) {
47 var tabContents = document.querySelectorAll('#content > div');
48 var tabHeaders = $('navigation').querySelectorAll('.tab-header');
49 for (var i = 0; i != tabContents.length; i++) {
50 var tabContent = tabContents[i];
51 var tabHeader = tabHeaders[i];
52 if (tabContent.id == id) {
53 tabContent.classList.add('selected');
54 tabHeader.classList.add('selected');
55 } else {
56 tabContent.classList.remove('selected');
57 tabHeader.classList.remove('selected');
58 }
59 }
60}
61
Ben Murdochbbcdd452013-07-25 10:06:34 +010062function populateLists(data) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000063 removeChildren('pages');
64 removeChildren('extensions');
Ben Murdocha3f7b4e2013-07-24 10:36:34 +010065 removeChildren('apps');
Torne (Richard Coles)58218062012-11-14 11:43:16 +000066 removeChildren('others');
67
68 for (var i = 0; i < data.length; i++) {
69 if (data[i].type === 'page')
70 addToPagesList(data[i]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000071 else if (data[i].type === 'extension')
72 addToExtensionsList(data[i]);
Ben Murdocha3f7b4e2013-07-24 10:36:34 +010073 else if (data[i].type === 'app')
74 addToAppsList(data[i]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000075 else
76 addToOthersList(data[i]);
77 }
78}
79
Ben Murdochbbcdd452013-07-25 10:06:34 +010080function populateWorkersList(data) {
81 removeChildren('workers');
82
83 for (var i = 0; i < data.length; i++)
84 addToWorkersList(data[i]);
85}
86
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010087function populateDeviceLists(devices) {
Ben Murdochbb1529c2013-08-08 10:24:53 +010088 if (!devices)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010089 return;
Ben Murdoch9ab55632013-07-18 11:57:30 +010090
Ben Murdochbb1529c2013-08-08 10:24:53 +010091 function alreadyDisplayed(element, data) {
92 var json = JSON.stringify(data);
93 if (element.cachedJSON == json)
94 return true;
95 element.cachedJSON = json;
96 return false;
97 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010098
Ben Murdochbb1529c2013-08-08 10:24:53 +010099 var deviceList = $('devices');
100 if (alreadyDisplayed(deviceList, devices))
101 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000102
Ben Murdochbb1529c2013-08-08 10:24:53 +0100103 function removeObsolete(validIds, section) {
104 if (validIds.indexOf(section.id) < 0)
105 section.remove();
106 }
107
108 var newDeviceIds = devices.map(function(d) { return d.adbGlobalId });
109 Array.prototype.forEach.call(
110 deviceList.querySelectorAll('.device'),
111 removeObsolete.bind(null, newDeviceIds));
112
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100113 for (var d = 0; d < devices.length; d++) {
114 var device = devices[d];
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000115
Ben Murdochbb1529c2013-08-08 10:24:53 +0100116 var browserList;
117 var deviceSection = $(device.adbGlobalId);
118 if (deviceSection) {
119 browserList = deviceSection.querySelector('.browsers');
120 } else {
121 deviceSection = document.createElement('div');
122 deviceSection.id = device.adbGlobalId;
123 deviceSection.className = 'device list';
124 deviceList.appendChild(deviceSection);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000125
Ben Murdochbb1529c2013-08-08 10:24:53 +0100126 var deviceHeader = document.createElement('div');
127 deviceHeader.className = 'section';
128 deviceHeader.textContent = device.adbModel;
129 deviceSection.appendChild(deviceHeader);
130
131 browserList = document.createElement('div');
132 browserList.className = 'browsers';
133 deviceSection.appendChild(browserList);
134 }
135
136 if (alreadyDisplayed(deviceSection, device))
137 continue;
138
139 var newBrowserIds =
140 device.browsers.map(function(b) { return b.adbGlobalId });
141 Array.prototype.forEach.call(
142 browserList.querySelectorAll('.browser'),
143 removeObsolete.bind(null, newBrowserIds));
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100144
145 for (var b = 0; b < device.browsers.length; b++) {
146 var browser = device.browsers[b];
147
Ben Murdochbb1529c2013-08-08 10:24:53 +0100148 var pageList;
149 var browserSection = $(browser.adbGlobalId);
150 if (browserSection) {
151 pageList = browserSection.querySelector('.pages');
152 pageList.textContent = '';
153 } else {
154 browserSection = document.createElement('div');
155 browserSection.id = browser.adbGlobalId;
156 browserSection.className = 'browser';
157 browserList.appendChild(browserSection);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100158
Ben Murdochbb1529c2013-08-08 10:24:53 +0100159 var browserHeader = document.createElement('div');
160 browserHeader.className = 'small-section';
161 browserHeader.textContent = browser.adbBrowserName;
162 browserSection.appendChild(browserHeader);
163
164 var newPage = document.createElement('div');
165 newPage.className = 'open';
166
167 var newPageUrl = document.createElement('input');
168 newPageUrl.type = 'text';
169 newPageUrl.placeholder = 'Open tab with url';
170 newPage.appendChild(newPageUrl);
171
172 var openHandler = function(browserId, input) {
173 open(browserId, input.value || 'about:blank');
174 input.value = '';
175 }.bind(null, browser.adbGlobalId, newPageUrl);
176 newPageUrl.addEventListener('keyup', function(handler, event) {
177 if (event.keyIdentifier == 'Enter' && event.target.value)
178 handler();
179 }.bind(null, openHandler), true);
180
181 var newPageButton = document.createElement('button');
182 newPageButton.textContent = 'Open';
183 newPage.appendChild(newPageButton);
184 newPageButton.addEventListener('click', openHandler, true);
185
186 browserSection.appendChild(newPage);
187
188 pageList = document.createElement('div');
189 pageList.className = 'list pages';
190 browserSection.appendChild(pageList);
191 }
192
193 if (alreadyDisplayed(browserSection, browser))
194 continue;
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100195
196 for (var p = 0; p < browser.pages.length; p++) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100197 var page = browser.pages[p];
198 var row = addTargetToList(
199 page, pageList, ['faviconUrl', 'name', 'url']);
200 row.appendChild(createActionLink(
201 'reload', reload.bind(null, page), page.attached));
202 row.appendChild(createActionLink(
203 'close', terminate.bind(null, page), page.attached));
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100204 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000205 }
206 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000207}
208
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000209function addToPagesList(data) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100210 addTargetToList(data, $('pages'), ['faviconUrl', 'name', 'url']);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000211}
212
213function addToExtensionsList(data) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100214 addTargetToList(data, $('extensions'), ['name', 'url']);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000215}
216
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100217function addToAppsList(data) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100218 addTargetToList(data, $('apps'), ['name', 'url']);
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100219}
220
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000221function addToWorkersList(data) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100222 var row = addTargetToList(data, $('workers'), ['name', 'url', 'pid']);
223 row.appendChild(createActionLink(
224 'terminate', terminate.bind(null, data), data.attached));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000225}
226
227function addToOthersList(data) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100228 addTargetToList(data, $('others'), ['url']);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000229}
230
231function formatValue(data, property) {
232 var value = data[property];
233
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000234 if (property == 'faviconUrl') {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000235 var faviconElement = document.createElement('img');
236 if (value)
237 faviconElement.src = value;
238 return faviconElement;
239 }
240
241 var text = value ? String(value) : '';
242 if (text.length > 100)
243 text = text.substring(0, 100) + '\u2026';
244
245 if (property == 'pid')
246 text = 'Pid:' + text;
247
248 var span = document.createElement('span');
249 span.textContent = ' ' + text + ' ';
250 span.className = property;
251 return span;
252}
253
Ben Murdochbb1529c2013-08-08 10:24:53 +0100254function addTargetToList(data, list, properties) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000255 var row = document.createElement('div');
256 row.className = 'row';
257 for (var j = 0; j < properties.length; j++)
258 row.appendChild(formatValue(data, properties[j]));
259
Ben Murdochbb1529c2013-08-08 10:24:53 +0100260 row.appendChild(createActionLink('inspect', inspect.bind(null, data)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000261
262 row.processId = data.processId;
263 row.routeId = data.routeId;
264
265 list.appendChild(row);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100266 return row;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000267}
268
Ben Murdochbb1529c2013-08-08 10:24:53 +0100269function createActionLink(text, handler, opt_disabled) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000270 var link = document.createElement('a');
Ben Murdochbb1529c2013-08-08 10:24:53 +0100271 if (opt_disabled)
272 link.classList.add('disabled');
273 else
274 link.classList.remove('disabled');
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000275
276 link.setAttribute('href', '#');
Ben Murdochbb1529c2013-08-08 10:24:53 +0100277 link.textContent = text;
278 link.addEventListener('click', handler, true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000279 return link;
280}
281
Ben Murdoch2385ea32013-08-06 11:01:04 +0100282
283function initPortForwarding() {
284 $('port-forwarding-enable').addEventListener('change', enablePortForwarding);
285
286 $('port-forwarding-config-open').addEventListener(
287 'click', openPortForwardingConfig);
288 $('port-forwarding-config-close').addEventListener(
289 'click', closePortForwardingConfig);
290 $('port-forwarding-config-done').addEventListener(
291 'click', commitPortForwardingConfig);
292}
293
294function enablePortForwarding(event) {
295 chrome.send('set-port-forwarding-enabled', [event.target.checked]);
296}
297
298function handleKey(event) {
299 switch (event.keyCode) {
300 case 13: // Enter
301 if (event.target.nodeName == 'INPUT') {
302 var line = event.target.parentNode;
303 if (!line.classList.contains('fresh') ||
304 line.classList.contains('empty'))
305 commitPortForwardingConfig();
306 else
307 commitFreshLineIfValid(true /* select new line */);
308 } else {
309 commitPortForwardingConfig();
310 }
311 break;
312
313 case 27:
314 closePortForwardingConfig();
315 break;
316 }
317}
318
319function openPortForwardingConfig() {
320 loadPortForwardingConfig(window.portForwardingConfig);
321
322 $('port-forwarding-overlay').classList.add('open');
323 document.addEventListener('keyup', handleKey);
324
325 var freshPort = document.querySelector('.fresh .port');
326 if (freshPort)
327 freshPort.focus();
328 else
329 $('port-forwarding-config-done').focus();
330}
331
332function closePortForwardingConfig() {
333 $('port-forwarding-overlay').classList.remove('open');
334 document.removeEventListener('keyup', handleKey);
335}
336
337function loadPortForwardingConfig(config) {
338 var list = $('port-forwarding-config-list');
339 list.textContent = '';
340 for (var port in config)
341 list.appendChild(createConfigLine(port, config[port]));
342 list.appendChild(createEmptyConfigLine());
343}
344
345function commitPortForwardingConfig() {
346 if (document.querySelector(
347 '.port-forwarding-pair:not(.fresh) input.invalid'))
348 return;
349
350 if (document.querySelector(
351 '.port-forwarding-pair.fresh:not(.empty) input.invalid'))
352 return;
353
354 closePortForwardingConfig();
355 commitFreshLineIfValid();
356 var lines = document.querySelectorAll('.port-forwarding-pair');
357 var config = {};
358 for (var i = 0; i != lines.length; i++) {
359 var line = lines[i];
360 var portInput = line.querySelector('.port:not(.invalid)');
361 var locationInput = line.querySelector('.location:not(.invalid)');
362 if (portInput && locationInput)
363 config[portInput.value] = locationInput.value;
364 }
365 chrome.send('set-port-forwarding-config', [config]);
366}
367
368function updatePortForwardingEnabled(enabled) {
369 var checkbox = $('port-forwarding-enable');
370 checkbox.checked = !!enabled;
371 checkbox.disabled = false;
372}
373
374function updatePortForwardingConfig(config) {
375 window.portForwardingConfig = config;
376 $('port-forwarding-config-open').disabled = !config;
377}
378
379function createConfigLine(port, location) {
380 var line = document.createElement('div');
381 line.className = 'port-forwarding-pair';
382
383 var portInput = createConfigField(port, 'port', 'Port', validatePort);
384 line.appendChild(portInput);
385
386 var locationInput = createConfigField(
387 location, 'location', 'IP address and port', validateLocation);
388 line.appendChild(locationInput);
389 locationInput.addEventListener('keydown', function(e) {
390 if (e.keyIdentifier == 'U+0009' && // Tab
391 !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey &&
392 line.classList.contains('fresh') &&
393 !line.classList.contains('empty')) {
394 // Tabbing forward on the fresh line, try create a new empty one.
395 commitFreshLineIfValid(true);
396 e.preventDefault();
397 }
398 });
399
400 var lineDelete = document.createElement('div');
401 lineDelete.className = 'close-button';
402 lineDelete.addEventListener('click', function() {
403 var newSelection = line.nextElementSibling;
404 line.parentNode.removeChild(line);
405 selectLine(newSelection);
406 });
407 line.appendChild(lineDelete);
408
409 line.addEventListener('click', selectLine.bind(null, line));
410 line.addEventListener('focus', selectLine.bind(null, line));
411
412 checkEmptyLine(line);
413
414 return line;
415}
416
417function validatePort(input) {
418 var match = input.value.match(/^(\d+)$/);
419 if (!match)
420 return false;
421 var port = parseInt(match[1]);
422 if (port < 5000 || 10000 < port)
423 return false;
424
425 var inputs = document.querySelectorAll('input.port:not(.invalid)');
426 for (var i = 0; i != inputs.length; ++i) {
427 if (inputs[i] == input)
428 break;
429 if (parseInt(inputs[i].value) == port)
430 return false;
431 }
432 return true;
433}
434
435function validateLocation(input) {
436 var match = input.value.match(/^([a-zA-Z0-9\.]+):(\d+)$/);
437 if (!match)
438 return false;
439 var port = parseInt(match[2]);
440 return port <= 10000;
441}
442
443function createEmptyConfigLine() {
444 var line = createConfigLine('', '');
445 line.classList.add('fresh');
446 return line;
447}
448
449function createConfigField(value, className, hint, validate) {
450 var input = document.createElement('input');
451 input.className = className;
452 input.type = 'text';
453 input.placeholder = hint;
454 input.value = value;
455
456 function checkInput() {
457 if (validate(input))
458 input.classList.remove('invalid');
459 else
460 input.classList.add('invalid');
461 if (input.parentNode)
462 checkEmptyLine(input.parentNode);
463 }
464 checkInput();
465
466 input.addEventListener('keyup', checkInput);
467 input.addEventListener('focus', function() {
468 selectLine(input.parentNode);
469 });
470
471 return input;
472}
473
474function checkEmptyLine(line) {
475 var inputs = line.querySelectorAll('input');
476 var empty = true;
477 for (var i = 0; i != inputs.length; i++) {
478 if (inputs[i].value != '')
479 empty = false;
480 }
481 if (empty)
482 line.classList.add('empty');
483 else
484 line.classList.remove('empty');
485}
486
487function selectLine(line) {
488 if (line.classList.contains('selected'))
489 return;
490 unselectLine();
491 line.classList.add('selected');
492}
493
494function unselectLine() {
495 var line = document.querySelector('.port-forwarding-pair.selected');
496 if (!line)
497 return;
498 line.classList.remove('selected');
499 commitFreshLineIfValid();
500}
501
502function commitFreshLineIfValid(opt_selectNew) {
503 var line = document.querySelector('.port-forwarding-pair.fresh');
504 if (line.querySelector('.invalid'))
505 return;
506 line.classList.remove('fresh');
507 var freshLine = createEmptyConfigLine();
508 line.parentNode.appendChild(freshLine);
509 if (opt_selectNew)
510 freshLine.querySelector('.port').focus();
511}
512
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000513document.addEventListener('DOMContentLoaded', onload);