summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorjamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-23 17:53:39 +0000
committerjamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-23 17:53:39 +0000
commitb70a2a5e9acb924cc9ac700616f6b25852132fa5 (patch)
tree074b1a29f9624593c8f2ec3cd6782292f9f74433 /remoting
parent17c38e072b83cf5d97ba00b5df194e09a949c4c4 (diff)
downloadchromium_src-b70a2a5e9acb924cc9ac700616f6b25852132fa5.zip
chromium_src-b70a2a5e9acb924cc9ac700616f6b25852132fa5.tar.gz
chromium_src-b70a2a5e9acb924cc9ac700616f6b25852132fa5.tar.bz2
Paired client revocation UI.
This CL implements the HTML UI for displaying and revoking pairings. It was previously part of https://chromiumcodereview.appspot.com/18225004, but most of that has now been landed as separate CLs. BUG=156182 Review URL: https://chromiumcodereview.appspot.com/19824003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213161 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/plugin/host_script_object.h4
-rw-r--r--remoting/host/remoting_me2me_host.cc9
-rw-r--r--remoting/remoting.gyp1
-rw-r--r--remoting/resources/remoting_strings.grd27
-rw-r--r--remoting/webapp/all_js_load.gtestjs2
-rw-r--r--remoting/webapp/appsv2.patch4
-rw-r--r--remoting/webapp/client_screen.js20
-rw-r--r--remoting/webapp/event_handlers.js6
-rw-r--r--remoting/webapp/host_controller.js37
-rw-r--r--remoting/webapp/host_dispatcher.js34
-rw-r--r--remoting/webapp/main.css17
-rw-r--r--remoting/webapp/main.html58
-rw-r--r--remoting/webapp/paired_client_manager.js215
-rw-r--r--remoting/webapp/remoting.js25
-rw-r--r--remoting/webapp/ui_mode.js1
15 files changed, 431 insertions, 29 deletions
diff --git a/remoting/host/plugin/host_script_object.h b/remoting/host/plugin/host_script_object.h
index 9f8a867..a3d5bcd 100644
--- a/remoting/host/plugin/host_script_object.h
+++ b/remoting/host/plugin/host_script_object.h
@@ -242,10 +242,6 @@ class HostNPScriptObject {
// success status.
void InvokeBooleanCallback(const ScopedRefNPObject& callback, bool result);
- // Callback handler for DaemonController::DeletePairedClients().
- void InvokeDeletePairedClientsCallback(const ScopedRefNPObject& callback,
- bool success);
-
// Callback handler for DaemonController::GetConfig().
void InvokeGetDaemonConfigCallback(const ScopedRefNPObject& callback,
scoped_ptr<base::DictionaryValue> config);
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 28e380a7..7b7d3e3 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -481,9 +481,12 @@ void HostProcess::CreateAuthenticatorFactory() {
return;
}
- // TODO(jamiewalch): Create a pairing registry here once all the code
- // is committed.
- scoped_refptr<remoting::protocol::PairingRegistry> pairing_registry = NULL;
+ scoped_refptr<protocol::PairingRegistry> pairing_registry = NULL;
+ scoped_ptr<protocol::PairingRegistry::Delegate> delegate(
+ CreatePairingRegistryDelegate(context_->file_task_runner()));
+ if (delegate) {
+ pairing_registry = new protocol::PairingRegistry(delegate.Pass());
+ }
scoped_ptr<protocol::AuthenticatorFactory> factory;
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index c649f90..fb52c13 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -2169,6 +2169,7 @@
'webapp/host_setup_dialog.js',
'webapp/main.html',
'webapp/manifest.json',
+ 'webapp/paired_client_manager.js',
'webapp/remoting.js',
],
},
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 18c3625..3c69d18 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -624,6 +624,33 @@
<message name="IDR_HOST_STARTED" desc="The message reported to the EventLog by Chromoting Host every time it is started.">
Host started for user: <ph name="HOST_USERNAME">%1<ex>host@email.com</ex></ph>.
</message>
+ <message desc="Link to allow a host's paired clients to be viewed or edited." name="IDR_HOME_DAEMON_MANAGE_PAIRINGS">
+ View/edit
+ </message>
+ <message desc="Link to delete a specific paired client." name="IDR_DELETE_PAIRED_CLIENT">
+ Delete
+ </message>
+ <message desc="Link to delete all paired clients." name="IDR_DELETE_ALL_PAIRED_CLIENTS">
+ Delete all
+ </message>
+ <message desc="Message displayed in the paired client manager dialog when all paired clients have been deleted." name="IDR_NO_PAIRED_CLIENTS">
+ All paired clients have been deleted.
+ </message>
+ <message desc="Message displayed when the current computer has been paired with one or more clients, allowing them to connect without needing a PIN." name="IDR_HOME_DAEMON_PAIRED_MESSAGE">
+ This computer is configured to allow one or more clients to connect without entering a PIN.
+ </message>
+ <message desc="Message displayed above the list of paired clients, explaining its purpose." name="IDR_PAIRED_CLIENTS_INTRODUCTION">
+ The following clients have been paired with this computer and can connect without supplying a PIN. You can revoke this permission at any time, either individually, or for all clients.
+ </message>
+ <message desc="Table header for the dates at which clients were paired." name="IDR_PAIRED_CLIENT_DATE">
+ Pairing date
+ </message>
+ <message desc="Table header for the names of paired clients." name="IDR_PAIRED_CLIENT_NAME">
+ Client
+ </message>
+ <message desc="Text shown while a background operation is in progress." name="IDR_WORKING">
+ Working…
+ </message>
</messages>
</release>
</grit>
diff --git a/remoting/webapp/all_js_load.gtestjs b/remoting/webapp/all_js_load.gtestjs
index 7b29e03..61a8aa4 100644
--- a/remoting/webapp/all_js_load.gtestjs
+++ b/remoting/webapp/all_js_load.gtestjs
@@ -52,7 +52,6 @@ AllJsLoadTest.prototype = {
'oauth2.js',
'paired_client_manager.js',
'plugin_settings.js',
- 'xhr_proxy.js',
'remoting.js',
'session_connector.js',
'server_log_entry.js',
@@ -66,6 +65,7 @@ AllJsLoadTest.prototype = {
'wcs_sandbox_content.js',
'wcs_sandbox_container.js',
'xhr.js',
+ 'xhr_proxy.js',
],
};
diff --git a/remoting/webapp/appsv2.patch b/remoting/webapp/appsv2.patch
index 3305207..a214d0c 100644
--- a/remoting/webapp/appsv2.patch
+++ b/remoting/webapp/appsv2.patch
@@ -121,7 +121,7 @@ index f89072a..2fadd83 100644
remoting.stats = new remoting.ConnectionStats(
document.getElementById('statistics'));
remoting.formatIq = new remoting.FormatIq();
-@@ -156,9 +151,6 @@ remoting.initHomeScreenUi = function () {
+@@ -11,9 +151,6 @@ remoting.initHomeScreenUi = function() {
remoting.hostController = new remoting.HostController();
document.getElementById('share-button').disabled = !isIT2MeSupported_();
remoting.setMode(remoting.AppMode.HOME);
@@ -130,7 +130,7 @@ index f89072a..2fadd83 100644
- }
remoting.hostSetupDialog =
new remoting.HostSetupDialog(remoting.hostController);
- // Display the cached host list, then asynchronously update and re-display it.
+ var dialog = document.getElementById('paired-clients-list');
diff --git a/remoting/webapp/xhr_proxy.js b/remoting/webapp/xhr_proxy.js
index 4c45780..653b481 100644
--- a/xhr_proxy.js
diff --git a/remoting/webapp/client_screen.js b/remoting/webapp/client_screen.js
index 8ecde77..3698186 100644
--- a/remoting/webapp/client_screen.js
+++ b/remoting/webapp/client_screen.js
@@ -357,8 +357,22 @@ remoting.onConnected = function(clientSession) {
};
remoting.HostSettings.save(clientSession.hostId, pairingInfo);
};
- // TODO(jamiewalch): Since we can't get a descriptive name for the local
- // computer from Javascript, pass the empty string for now.
- clientSession.requestPairing('', onPairingComplete);
+ // Use the platform name as a proxy for the local computer name.
+ // TODO(jamiewalch): Use a descriptive name for the local computer, for
+ // example, its Chrome Sync name.
+ var clientName = '';
+ if (navigator.platform.indexOf('Mac') != -1) {
+ clientName = 'Mac';
+ } else if (navigator.platform.indexOf('Win32') != -1) {
+ clientName = 'Windows';
+ } else if (navigator.userAgent.match(/\bCrOS\b/)) {
+ clientName = 'ChromeOS';
+ } else if (navigator.platform.indexOf('Linux') != -1) {
+ clientName = 'Linux';
+ } else {
+ console.log('Unrecognized client platform. Using navigator.platform.');
+ clientName = navigator.platform;
+ }
+ clientSession.requestPairing(clientName, onPairingComplete);
}
};
diff --git a/remoting/webapp/event_handlers.js b/remoting/webapp/event_handlers.js
index a481e19..47e6d53 100644
--- a/remoting/webapp/event_handlers.js
+++ b/remoting/webapp/event_handlers.js
@@ -85,7 +85,11 @@ function onLoad() {
{ event: 'click', id: 'host-config-done-dismiss', fn: goHome },
{ event: 'click', id: 'host-config-error-dismiss', fn: goHome },
{ event: 'click', id: 'token-refresh-error-ok', fn: goHome },
- { event: 'click', id: 'token-refresh-error-sign-in', fn: doAuthRedirect }
+ { event: 'click', id: 'token-refresh-error-sign-in', fn: doAuthRedirect },
+ { event: 'click', id: 'open-paired-client-manager-dialog',
+ fn: remoting.setMode.bind(null,
+ remoting.AppMode.HOME_MANAGE_PAIRINGS) },
+ { event: 'click', id: 'close-paired-client-manager-dialog', fn: goHome }
];
for (var i = 0; i < actions.length; ++i) {
diff --git a/remoting/webapp/host_controller.js b/remoting/webapp/host_controller.js
index de1a0a9..f728a32 100644
--- a/remoting/webapp/host_controller.js
+++ b/remoting/webapp/host_controller.js
@@ -337,5 +337,42 @@ remoting.HostController.prototype.getLocalHostId = function(onDone) {
});
};
+/**
+ * Fetch the list of paired clients for this host.
+ *
+ * @param {function(Array.<remoting.PairedClient>):void} onDone
+ * @param {function(remoting.Error):void} onError
+ * @return {void}
+ */
+remoting.HostController.prototype.getPairedClients = function(onDone,
+ onError) {
+ this.hostDispatcher_.getPairedClients(onDone, onError);
+};
+
+/**
+ * Delete a single paired client.
+ *
+ * @param {string} client The client id of the pairing to delete.
+ * @param {function():void} onDone Completion callback.
+ * @param {function(remoting.Error):void} onError Error callback.
+ * @return {void}
+ */
+remoting.HostController.prototype.deletePairedClient = function(
+ client, onDone, onError) {
+ this.hostDispatcher_.deletePairedClient(client, onDone, onError);
+};
+
+/**
+ * Delete all paired clients.
+ *
+ * @param {function():void} onDone Completion callback.
+ * @param {function(remoting.Error):void} onError Error callback.
+ * @return {void}
+ */
+remoting.HostController.prototype.clearPairedClients = function(
+ onDone, onError) {
+ this.hostDispatcher_.clearPairedClients(onDone, onError);
+};
+
/** @type {remoting.HostController} */
remoting.hostController = null;
diff --git a/remoting/webapp/host_dispatcher.js b/remoting/webapp/host_dispatcher.js
index 7b81c0e..7841eb6 100644
--- a/remoting/webapp/host_dispatcher.js
+++ b/remoting/webapp/host_dispatcher.js
@@ -382,23 +382,43 @@ remoting.HostDispatcher.prototype.getPairedClients = function(callback,
};
/**
- * @param {function(boolean):void} onDone
+ * The pairing API returns a boolean to indicate success or failure, but
+ * the JS API is defined in terms of onDone and onError callbacks. This
+ * function converts one to the other.
+ *
+ * @param {function():void} onDone Success callback.
+ * @param {function(remoting.Error):void} onError Error callback.
+ * @param {boolean} success True if the operation succeeded; false otherwise.
+ * @private
+ */
+remoting.HostDispatcher.runCallback_ = function(onDone, onError, success) {
+ if (success) {
+ onDone();
+ } else {
+ onError(remoting.Error.UNEXPECTED);
+ }
+};
+
+/**
+ * @param {function():void} onDone
* @param {function(remoting.Error):void} onError
* @return {void}
*/
remoting.HostDispatcher.prototype.clearPairedClients =
function(onDone, onError) {
+ var callback =
+ remoting.HostDispatcher.runCallback_.bind(null, onDone, onError);
switch (this.state_) {
case remoting.HostDispatcher.State.UNKNOWN:
this.pendingRequests_.push(
this.clearPairedClients.bind(this, onDone, onError));
break;
case remoting.HostDispatcher.State.NATIVE_MESSAGING:
- this.nativeMessagingHost_.clearPairedClients(onDone, onError);
+ this.nativeMessagingHost_.clearPairedClients(callback, onError);
break;
case remoting.HostDispatcher.State.NPAPI:
try {
- this.npapiHost_.clearPairedClients(onDone);
+ this.npapiHost_.clearPairedClients(callback);
} catch (err) {
onError(remoting.Error.MISSING_PLUGIN);
}
@@ -408,23 +428,25 @@ remoting.HostDispatcher.prototype.clearPairedClients =
/**
* @param {string} client
- * @param {function(boolean):void} onDone
+ * @param {function():void} onDone
* @param {function(remoting.Error):void} onError
* @return {void}
*/
remoting.HostDispatcher.prototype.deletePairedClient =
function(client, onDone, onError) {
+ var callback =
+ remoting.HostDispatcher.runCallback_.bind(null, onDone, onError);
switch (this.state_) {
case remoting.HostDispatcher.State.UNKNOWN:
this.pendingRequests_.push(
this.deletePairedClient.bind(this, client, onDone, onError));
break;
case remoting.HostDispatcher.State.NATIVE_MESSAGING:
- this.nativeMessagingHost_.deletePairedClient(client, onDone, onError);
+ this.nativeMessagingHost_.deletePairedClient(client, callback, onError);
break;
case remoting.HostDispatcher.State.NPAPI:
try {
- this.npapiHost_.deletePairedClient(client, onDone);
+ this.npapiHost_.deletePairedClient(client, callback);
} catch (err) {
onError(remoting.Error.MISSING_PLUGIN);
}
diff --git a/remoting/webapp/main.css b/remoting/webapp/main.css
index 8faa94a..870796d 100644
--- a/remoting/webapp/main.css
+++ b/remoting/webapp/main.css
@@ -11,6 +11,11 @@ tfoot, thead, tr, th, td, button {
margin: 0;
padding: 0;
border: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
}
body {
@@ -297,6 +302,10 @@ section {
margin-left: 5px;
}
+.button-row span:first-child {
+ width: 100%;
+}
+
.clickable {
cursor: pointer;
}
@@ -424,6 +433,10 @@ section {
padding-__MSG_@@bidi_start_edge__: 2px;
}
+#paired-clients-list table {
+ width: 100%;
+}
+
.message {
margin-bottom: 24px;
}
@@ -440,6 +453,10 @@ td {
vertical-align: middle;
}
+thead {
+ font-weight: bold;
+}
+
.host-online.clickable:hover,
.host-online.clickable.child-focused {
background-color: #f2f2f2;
diff --git a/remoting/webapp/main.html b/remoting/webapp/main.html
index 4a97637..c352452 100644
--- a/remoting/webapp/main.html
+++ b/remoting/webapp/main.html
@@ -206,10 +206,17 @@ found in the LICENSE file.
</button>
</div> <!-- enabled -->
<div data-daemon-state="enabled">
- <span i18n-content="HOME_DAEMON_ACTIVE_MESSAGE"></span>
- <a id="change-daemon-pin"
- href="#"
- i18n-content="HOME_DAEMON_CHANGE_PIN_LINK"></a>
+ <div>
+ <span i18n-content="HOME_DAEMON_ACTIVE_MESSAGE"></span>
+ <a id="change-daemon-pin"
+ href="#"
+ i18n-content="HOME_DAEMON_CHANGE_PIN_LINK"></a>
+ </div>
+ <div id="paired-client-manager-message" hidden>
+ <span i18n-content="HOME_DAEMON_PAIRED_MESSAGE"></span>
+ <a href="#"
+ id="open-paired-client-manager-dialog"
+ i18n-content="HOME_DAEMON_MANAGE_PAIRINGS"></a>
</div>
</div> <!-- daemon-control -->
<div id="host-list-empty" hidden>
@@ -250,11 +257,11 @@ found in the LICENSE file.
</div> <!-- auth-dialog -->
<div class="dialog-screen"
- data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed"
+ data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed home.manage-pairings"
hidden></div>
<div class="dialog-container"
- data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed"
+ data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed home.manage-pairings"
hidden>
<div class="box-spacer"></div>
@@ -661,6 +668,45 @@ found in the LICENSE file.
</div>
</div> <!-- home.confirm-host-delete -->
+ <div id="paired-client-manager-dialog"
+ class="kd-modaldialog"
+ data-ui-mode="home.manage-pairings"
+ hidden>
+ <p i18n-content="PAIRED_CLIENTS_INTRODUCTION"
+ class="message">
+ </p>
+ <div id="paired-clients-list">
+ <table>
+ <thead>
+ <tr>
+ <td i18n-content="PAIRED_CLIENT_DATE"></td>
+ <td i18n-content="PAIRED_CLIENT_NAME"></td>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+ </table>
+ <p id="no-paired-clients">
+ <em i18n-content="NO_PAIRED_CLIENTS"></em>
+ </p>
+ </div> <!-- paired-clients-list -->
+ <p id="paired-client-manager-dialog-error"
+ class="error-state"
+ hidden>
+ </p>
+ <div class="button-row">
+ <span id="paired-client-manager-dialog-working"
+ class="waiting"
+ i18n-content="WORKING"
+ hidden></span>
+ <button id="delete-all-paired-clients"
+ i18n-content="DELETE_ALL_PAIRED_CLIENTS">
+ <button id="close-paired-client-manager-dialog"
+ i18n-content="CLOSE">
+ </button>
+ </div>
+ </div> <!-- home.manage-pairings -->
+
<div class="box-spacer"></div>
</div> <!-- dialog-container -->
diff --git a/remoting/webapp/paired_client_manager.js b/remoting/webapp/paired_client_manager.js
index 636716d..05d6fe9 100644
--- a/remoting/webapp/paired_client_manager.js
+++ b/remoting/webapp/paired_client_manager.js
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+/**
+ * @fileoverview
+ * Dialog for showing the list of clients that are paired with this host.
+ */
+
'use strict';
/** @suppress {duplicate} */
@@ -24,6 +29,50 @@ remoting.PairedClient = function(pairedClient) {
this.clientId = /** @type {string} */ (pairedClient['clientId']);
this.clientName = /** @type {string} */ (pairedClient['clientName']);
this.createdTime = /** @type {number} */ (pairedClient['createdTime']);
+
+ /** @type {Element} */
+ this.tableRow = null;
+ /** @type {Element} */
+ this.deleteButton = null;
+};
+
+/**
+ * Create the DOM elements representing this client in the paired client
+ * manager dialog.
+ *
+ * @param {remoting.PairedClientManager} parent The paired client manager
+ * dialog containing this row.
+ * @param {Element} tbody The <tbody> element to which to append the row.
+ */
+remoting.PairedClient.prototype.createDom = function(parent, tbody) {
+ this.tableRow = document.createElement('tr');
+ var td = document.createElement('td');
+ td.innerText = new Date(this.createdTime).toLocaleDateString();
+ this.tableRow.appendChild(td);
+ td = document.createElement('td');
+ td.innerText = this.clientName;
+ this.tableRow.appendChild(td);
+ td = document.createElement('td');
+ this.deleteButton = document.createElement('a');
+ this.deleteButton.href = '#';
+ this.deleteButton.innerText = chrome.i18n.getMessage(
+ /*i18n-content*/'DELETE_PAIRED_CLIENT');
+ this.deleteButton.addEventListener(
+ 'click',
+ parent.deletePairedClient.bind(parent, this),
+ false);
+ td.appendChild(this.deleteButton);
+ this.tableRow.appendChild(td);
+ tbody.appendChild(this.tableRow);
+};
+
+/**
+ * Show or hide the "Delete" button for this row.
+ *
+ * @param {boolean} show True to show the button; false to hide it.
+ */
+remoting.PairedClient.prototype.showButton = function(show) {
+ this.deleteButton.hidden = !show;
};
/**
@@ -45,8 +94,7 @@ remoting.PairedClient.prototype.isValid = function() {
*/
remoting.PairedClient.convertToPairedClientArray = function(pairedClients) {
if (!(pairedClients instanceof Array)) {
- console.error('pairedClients is not an Array: ' +
- pairedClients);
+ console.error('pairedClients is not an Array:', pairedClients);
return null;
}
@@ -62,3 +110,166 @@ remoting.PairedClient.convertToPairedClientArray = function(pairedClients) {
}
return result;
}
+
+/**
+ * @param {remoting.HostController} hostController
+ * @param {HTMLElement} listContainer HTML <div> to contain the list of paired
+ * clients.
+ * @param {HTMLElement} message HTML <div> containing the message notifying
+ * the user that clients are paired and containing the link to open the
+ * dialog.
+ * @param {HTMLElement} deleteAllButton HTML <button> inititating the "delete
+ * all" action.
+ * @param {HTMLElement} closeButton HTML <button> to close the dialog.
+ * @param {HTMLElement} noPairedClients HTML <div> containing a message shown
+ * when all clients have been deleted.
+ * @param {HTMLElement} workingSpinner HTML element containing a spinner
+ * graphic shown while a deletion is in progress.
+ * @param {HTMLElement} errorDiv HTML <div> containing an error message shown
+ * if a delete operation fails.
+ * @constructor
+ */
+remoting.PairedClientManager = function(hostController, listContainer, message,
+ deleteAllButton, closeButton,
+ noPairedClients, workingSpinner,
+ errorDiv) {
+ /**
+ * @private
+ */
+ this.hostController_ = hostController;
+ /**
+ * @private
+ */
+ this.message_ = message;
+ /**
+ * @private
+ */
+ this.deleteAllButton_ = deleteAllButton;
+ /**
+ * @private
+ */
+ this.closeButton_ = closeButton;
+ /**
+ * @private
+ */
+ this.noPairedClients_ = noPairedClients;
+ /**
+ * @private
+ */
+ this.workingSpinner_ = workingSpinner;
+ /**
+ * @private
+ */
+ this.errorDiv_ = errorDiv;
+ /**
+ * @type {Element}
+ * @private
+ */
+ this.clientRows_ = listContainer.querySelector('tbody');
+ /**
+ * @type {Array.<remoting.PairedClient>}
+ */
+ this.pairedClients_ = [];
+
+ this.deleteAllButton_.addEventListener('click',
+ this.deleteAll_.bind(this),
+ false);
+};
+
+/**
+ * Populate the dialog with the list of paired clients and show or hide the
+ * message as appropriate.
+ *
+ * @param {*} pairedClients The list of paired clients as returned by the
+ * native host component.
+ * @return {void} Nothing.
+ */
+remoting.PairedClientManager.prototype.setPairedClients =
+ function(pairedClients) {
+ // Reset table.
+ while (this.clientRows_.lastChild) {
+ this.clientRows_.removeChild(this.clientRows_.lastChild);
+ }
+
+ this.pairedClients_ =
+ remoting.PairedClient.convertToPairedClientArray(pairedClients);
+ for (var i = 0; i < this.pairedClients_.length; ++i) {
+ var client = this.pairedClients_[i];
+ client.createDom(this, this.clientRows_);
+ }
+
+ // Show or hide the "this computer has paired clients" message.
+ this.setWorking_(false)
+};
+
+/**
+ * Enter or leave "working" mode. This indicates to the user that a delete
+ * operation is in progress. All dialog UI is disabled until it completes.
+ *
+ * @param {boolean} working True to enter "working" mode; false to leave it.
+ * @private
+ */
+remoting.PairedClientManager.prototype.setWorking_ = function(working) {
+ var hasPairedClients = (this.pairedClients_.length != 0);
+ for (var i = 0; i < this.pairedClients_.length; ++i) {
+ this.pairedClients_[i].showButton(!working);
+ }
+ this.closeButton_.disabled = working;
+ this.workingSpinner_.hidden = !working;
+ this.errorDiv_.hidden = true;
+ this.message_.hidden = !hasPairedClients;
+ this.deleteAllButton_.disabled = working || !hasPairedClients;
+ this.noPairedClients_.hidden = hasPairedClients;
+};
+
+/**
+ * Error callback for delete operations.
+ *
+ * @param {remoting.Error} error The error message.
+ * @private
+ */
+remoting.PairedClientManager.prototype.onError_ = function(error) {
+ this.setWorking_(false);
+ l10n.localizeElementFromTag(this.errorDiv_, error);
+ this.errorDiv_.hidden = false;
+};
+
+/**
+ * Delete a single paired client.
+ *
+ * @param {remoting.PairedClient} client The pairing to delete.
+ */
+remoting.PairedClientManager.prototype.deletePairedClient = function(client) {
+ this.setWorking_(true);
+ this.hostController_.deletePairedClient(client.clientId,
+ this.setWorking_.bind(this, false),
+ this.onError_.bind(this));
+ this.clientRows_.removeChild(client.tableRow);
+ for (var i = 0; i < this.pairedClients_.length; ++i) {
+ if (this.pairedClients_[i] == client) {
+ this.pairedClients_.splice(i, 1);
+ break;
+ }
+ }
+};
+
+/**
+ * Delete all paired clients.
+ *
+ * @private
+ */
+remoting.PairedClientManager.prototype.deleteAll_ = function() {
+ this.setWorking_(true);
+ this.hostController_.clearPairedClients(
+ this.setWorking_.bind(this, false),
+ this.onError_.bind(this));
+
+ while (this.clientRows_.lastChild) {
+ this.clientRows_.removeChild(this.clientRows_.lastChild);
+ }
+ this.pairedClients_ = [];
+};
+
+
+/** @type {remoting.PairedClientManager} */
+remoting.pairedClientManager = null;
diff --git a/remoting/webapp/remoting.js b/remoting/webapp/remoting.js
index 8d97f73..50fe034 100644
--- a/remoting/webapp/remoting.js
+++ b/remoting/webapp/remoting.js
@@ -156,6 +156,17 @@ remoting.initHomeScreenUi = function() {
}
remoting.hostSetupDialog =
new remoting.HostSetupDialog(remoting.hostController);
+ var dialog = document.getElementById('paired-clients-list');
+ var message = document.getElementById('paired-client-manager-message');
+ var deleteAll = document.getElementById('delete-all-paired-clients');
+ var close = document.getElementById('close-paired-client-manager-dialog');
+ var working = document.getElementById('paired-client-manager-dialog-working');
+ var error = document.getElementById('paired-client-manager-dialog-error');
+ var noPairedClients = document.getElementById('no-paired-clients');
+ remoting.pairedClientManager =
+ new remoting.PairedClientManager(remoting.hostController, dialog, message,
+ deleteAll, close, noPairedClients,
+ working, error);
// Display the cached host list, then asynchronously update and re-display it.
remoting.updateLocalHostState();
remoting.hostList.refresh(remoting.updateLocalHostState);
@@ -163,7 +174,7 @@ remoting.initHomeScreenUi = function() {
};
/**
- * Fetches local host state and updates host list accordingly.
+ * Fetches local host state and updates the DOM accordingly.
*/
remoting.updateLocalHostState = function() {
/**
@@ -182,7 +193,19 @@ remoting.updateLocalHostState = function() {
remoting.hostList.display();
};
+ /**
+ * @param {remoting.Error} error
+ */
+ var onError = function(error) {
+ console.error('Failed to get pairing status: ' + error);
+ remoting.pairedClientManager.setPairedClients([]);
+ }
+
remoting.hostController.getLocalHostId(onHostId);
+ remoting.hostController.getPairedClients(
+ remoting.pairedClientManager.setPairedClients.bind(
+ remoting.pairedClientManager),
+ onError);
};
/**
diff --git a/remoting/webapp/ui_mode.js b/remoting/webapp/ui_mode.js
index 4587d83..966207f 100644
--- a/remoting/webapp/ui_mode.js
+++ b/remoting/webapp/ui_mode.js
@@ -47,6 +47,7 @@ remoting.AppMode = {
HOST_SETUP_PROCESSING: 'home.host-setup.processing',
HOST_SETUP_DONE: 'home.host-setup.done',
HOST_SETUP_ERROR: 'home.host-setup.error',
+ HOME_MANAGE_PAIRINGS: 'home.manage-pairings',
IN_SESSION: 'in-session'
};