summaryrefslogtreecommitdiffstats
path: root/remoting/webapp
diff options
context:
space:
mode:
authorgarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-12 22:50:38 +0000
committergarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-12 22:50:38 +0000
commitef2f63388a322916314c332f5113b2fef9066789 (patch)
treed9b895558678c79870ac80cc1a753cdb59169f82 /remoting/webapp
parent0b024473aa7be95e830f9c9c971d9745f9004aa4 (diff)
downloadchromium_src-ef2f63388a322916314c332f5113b2fef9066789.zip
chromium_src-ef2f63388a322916314c332f5113b2fef9066789.tar.gz
chromium_src-ef2f63388a322916314c332f5113b2fef9066789.tar.bz2
[Remoting] Add generic JS typecheck functions to cleanup client_plugin.js.
Also add isValidState and isValidConnectionError so that the code is more readable. Also fix typo with supportedCapabilities BUG= R=jamiewalch@chromium.org Review URL: https://codereview.chromium.org/131643008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@250816 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/webapp')
-rw-r--r--remoting/webapp/client_plugin.js211
-rw-r--r--remoting/webapp/client_session.js23
-rw-r--r--remoting/webapp/host_controller.js22
-rw-r--r--remoting/webapp/host_it2me_native_messaging.js97
-rw-r--r--remoting/webapp/host_native_messaging.js229
-rw-r--r--remoting/webapp/host_session.js4
-rw-r--r--remoting/webapp/main.html1
-rw-r--r--remoting/webapp/typecheck.js128
8 files changed, 343 insertions, 372 deletions
diff --git a/remoting/webapp/client_plugin.js b/remoting/webapp/client_plugin.js
index 4826d8b..824098b 100644
--- a/remoting/webapp/client_plugin.js
+++ b/remoting/webapp/client_plugin.js
@@ -141,6 +141,19 @@ remoting.ClientPlugin.prototype.handleMessage_ = function(rawMessage) {
return;
}
+ try {
+ this.handleMessageMethod_(message);
+ } catch(e) {
+ console.error(/** @type {*} */ (e));
+ }
+}
+
+/**
+ * @param {{method:string, data:Object.<string,*>}}
+ * message Parsed message from the plugin.
+ * @private
+ */
+remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) {
/**
* Splits a string into a list of words delimited by spaces.
* @param {string} str String that should be split.
@@ -156,43 +169,27 @@ remoting.ClientPlugin.prototype.handleMessage_ = function(rawMessage) {
// Reset the size in case we had to enlarge it to support click-to-play.
this.plugin.width = 0;
this.plugin.height = 0;
- if (typeof message.data['apiVersion'] != 'number' ||
- typeof message.data['apiMinVersion'] != 'number') {
- console.error('Received invalid hello message:', rawMessage);
- return;
- }
- this.pluginApiVersion_ = /** @type {number} */ message.data['apiVersion'];
+ this.pluginApiVersion_ = getNumberAttr(message.data, 'apiVersion');
+ this.pluginApiMinVersion_ = getNumberAttr(message.data, 'apiMinVersion');
if (this.pluginApiVersion_ >= 7) {
- if (typeof message.data['apiFeatures'] != 'string') {
- console.error('Received invalid hello message:', rawMessage);
- return;
- }
this.pluginApiFeatures_ =
- tokenize((/** @type {string} */ message.data['apiFeatures']));
+ tokenize(getStringAttr(message.data, 'apiFeatures'));
// Negotiate capabilities.
/** @type {!Array.<string>} */
var requestedCapabilities = [];
if ('requestedCapabilities' in message.data) {
- if (typeof message.data['requestedCapabilities'] != 'string') {
- console.error('Received invalid hello message:', rawMessage);
- return;
- }
- requestedCapabilities = tokenize(
- (/** @type {string} */ message.data['requestedCapabilities']));
+ requestedCapabilities =
+ tokenize(getStringAttr(message.data, 'requestedCapabilities'));
}
/** @type {!Array.<string>} */
var supportedCapabilities = [];
if ('supportedCapabilities' in message.data) {
- if (typeof message.data['supportedCapabilities'] != 'string') {
- console.error('Received invalid hello message:', rawMessage);
- return;
- }
- supportedCapabilities = tokenize(
- (/** @type {string} */ message.data['requestedCapabilities']));
+ supportedCapabilities =
+ tokenize(getStringAttr(message.data, 'supportedCapabilities'));
}
// At the moment the webapp does not recognize any of
@@ -215,168 +212,106 @@ remoting.ClientPlugin.prototype.handleMessage_ = function(rawMessage) {
} else {
this.pluginApiFeatures_ = ['highQualityScaling'];
}
- this.pluginApiMinVersion_ =
- /** @type {number} */ message.data['apiMinVersion'];
this.helloReceived_ = true;
if (this.onInitializedCallback_ != null) {
this.onInitializedCallback_(true);
this.onInitializedCallback_ = null;
}
+
} else if (message.method == 'sendOutgoingIq') {
- if (typeof message.data['iq'] != 'string') {
- console.error('Received invalid sendOutgoingIq message:', rawMessage);
- return;
- }
- this.onOutgoingIqHandler((/** @type {string} */ message.data['iq']));
- } else if (message.method == 'logDebugMessage') {
- if (typeof message.data['message'] != 'string') {
- console.error('Received invalid logDebugMessage message:', rawMessage);
- return;
- }
- this.onDebugMessageHandler((/** @type {string} */ message.data['message']));
- } else if (message.method == 'onConnectionStatus') {
- if (typeof message.data['state'] != 'string' ||
- !remoting.ClientSession.State.hasOwnProperty(message.data['state']) ||
- typeof message.data['error'] != 'string') {
- console.error('Received invalid onConnectionState message:',
- rawMessage);
- return;
- }
+ this.onOutgoingIqHandler(getStringAttr(message.data, 'iq'));
- /** @type {remoting.ClientSession.State} */
- var state = remoting.ClientSession.State[message.data['state']];
- var error;
- if (remoting.ClientSession.ConnectionError.hasOwnProperty(
- message.data['error'])) {
- error = /** @type {remoting.ClientSession.ConnectionError} */
- remoting.ClientSession.ConnectionError[message.data['error']];
- } else {
- error = remoting.ClientSession.ConnectionError.UNKNOWN;
- }
+ } else if (message.method == 'logDebugMessage') {
+ this.onDebugMessageHandler(getStringAttr(message.data, 'message'));
+ } else if (message.method == 'onConnectionStatus') {
+ var state = remoting.ClientSession.State.fromString(
+ getStringAttr(message.data, 'state'))
+ var error = remoting.ClientSession.ConnectionError.fromString(
+ getStringAttr(message.data, 'error'));
this.onConnectionStatusUpdateHandler(state, error);
+
} else if (message.method == 'onDesktopSize') {
- if (typeof message.data['width'] != 'number' ||
- typeof message.data['height'] != 'number') {
- console.error('Received invalid onDesktopSize message:', rawMessage);
- return;
- }
- this.desktopWidth = /** @type {number} */ message.data['width'];
- this.desktopHeight = /** @type {number} */ message.data['height'];
- this.desktopXDpi = (typeof message.data['x_dpi'] == 'number') ?
- /** @type {number} */ (message.data['x_dpi']) : 96;
- this.desktopYDpi = (typeof message.data['y_dpi'] == 'number') ?
- /** @type {number} */ (message.data['y_dpi']) : 96;
+ this.desktopWidth = getNumberAttr(message.data, 'width');
+ this.desktopHeight = getNumberAttr(message.data, 'height');
+ this.desktopXDpi = getNumberAttr(message.data, 'x_dpi', 96);
+ this.desktopYDpi = getNumberAttr(message.data, 'y_dpi', 96);
this.onDesktopSizeUpdateHandler();
+
} else if (message.method == 'onPerfStats') {
- if (typeof message.data['videoBandwidth'] != 'number' ||
- typeof message.data['videoFrameRate'] != 'number' ||
- typeof message.data['captureLatency'] != 'number' ||
- typeof message.data['encodeLatency'] != 'number' ||
- typeof message.data['decodeLatency'] != 'number' ||
- typeof message.data['renderLatency'] != 'number' ||
- typeof message.data['roundtripLatency'] != 'number') {
- console.error('Received incorrect onPerfStats message:', rawMessage);
- return;
- }
+ // Return value is ignored. These calls will throw an error if the value
+ // is not a number.
+ getNumberAttr(message.data, 'videoBandwidth');
+ getNumberAttr(message.data, 'videoFrameRate');
+ getNumberAttr(message.data, 'captureLatency');
+ getNumberAttr(message.data, 'encodeLatency');
+ getNumberAttr(message.data, 'decodeLatency');
+ getNumberAttr(message.data, 'renderLatency');
+ getNumberAttr(message.data, 'roundtripLatency');
this.perfStats_ =
/** @type {remoting.ClientSession.PerfStats} */ message.data;
+
} else if (message.method == 'injectClipboardItem') {
- if (typeof message.data['mimeType'] != 'string' ||
- typeof message.data['item'] != 'string') {
- console.error('Received incorrect injectClipboardItem message.');
- return;
- }
+ var mimetype = getStringAttr(message.data, 'mimeType');
+ var item = getStringAttr(message.data, 'item');
if (remoting.clipboard) {
- remoting.clipboard.fromHost(
- (/** @type {string} */ message.data['mimeType']),
- (/** @type {string} */ message.data['item']));
+ remoting.clipboard.fromHost(mimetype, item);
}
+
} else if (message.method == 'onFirstFrameReceived') {
if (remoting.clientSession) {
remoting.clientSession.onFirstFrameReceived();
}
+
} else if (message.method == 'onConnectionReady') {
- if (typeof message.data['ready'] != 'boolean') {
- console.error('Received incorrect onConnectionReady message.');
- return;
- }
- var ready = /** @type {boolean} */ message.data['ready'];
+ var ready = getBooleanAttr(message.data, 'ready');
this.onConnectionReadyHandler(ready);
+
} else if (message.method == 'fetchPin') {
// The pairingSupported value in the dictionary indicates whether both
// client and host support pairing. If the client doesn't support pairing,
// then the value won't be there at all, so give it a default of false.
- /** @type {boolean} */
- var pairingSupported = false;
- if ('pairingSupported' in message.data) {
- pairingSupported =
- /** @type {boolean} */ message.data['pairingSupported'];
- if (typeof pairingSupported != 'boolean') {
- console.error('Received incorrect fetchPin message.');
- return;
- }
- }
+ var pairingSupported = getBooleanAttr(message.data, 'pairingSupported',
+ false)
this.fetchPinHandler(pairingSupported);
- } else if (message.method == 'setCapabilities') {
- if (typeof message.data['capabilities'] != 'string') {
- console.error('Received incorrect setCapabilities message.');
- return;
- }
+ } else if (message.method == 'setCapabilities') {
/** @type {!Array.<string>} */
- var capabilities =
- tokenize((/** @type {string} */ message.data['capabilities']));
+ var capabilities = tokenize(getStringAttr(message.data, 'capabilities'));
this.onSetCapabilitiesHandler(capabilities);
+
} else if (message.method == 'fetchThirdPartyToken') {
- if (typeof message.data['tokenUrl'] != 'string' ||
- typeof message.data['hostPublicKey'] != 'string' ||
- typeof message.data['scope'] != 'string') {
- console.error('Received incorrect fetchThirdPartyToken message.');
- return;
- }
- var tokenUrl = /** @type {string} */ message.data['tokenUrl'];
- var hostPublicKey =
- /** @type {string} */ message.data['hostPublicKey'];
- var scope = /** @type {string} */ message.data['scope'];
+ var tokenUrl = getStringAttr(message.data, 'tokenUrl');
+ var hostPublicKey = getStringAttr(message.data, 'hostPublicKey');
+ var scope = getStringAttr(message.data, 'scope');
this.fetchThirdPartyTokenHandler(tokenUrl, hostPublicKey, scope);
+
} else if (message.method == 'pairingResponse') {
- var clientId = /** @type {string} */ message.data['clientId'];
- var sharedSecret = /** @type {string} */ message.data['sharedSecret'];
- if (typeof clientId != 'string' || typeof sharedSecret != 'string') {
- console.error('Received incorrect pairingResponse message.');
- return;
- }
+ var clientId = getStringAttr(message.data, 'clientId');
+ var sharedSecret = getStringAttr(message.data, 'sharedSecret');
this.onPairingComplete_(clientId, sharedSecret);
+
} else if (message.method == 'extensionMessage') {
- if (typeof(message.data['type']) != 'string' ||
- typeof(message.data['data']) != 'string') {
- console.error('Invalid extension message:', message.data);
- return;
- }
- switch (message.data['type']) {
+ var extMsgType = getStringAttr(message, 'type');
+ var extMsgData = getStringAttr(message, 'data');
+ switch (extMsgType) {
case 'test-echo-reply':
- console.log('Got echo reply: ' + message.data['data']);
+ console.log('Got echo reply: ' + extMsgData);
break;
default:
- if (!this.onExtensionMessage_(
- (/** @type {string} */ message.data['type']),
- (/** @type {string} */ message.data['data']))) {
+ if (!this.onExtensionMessage_(extMsgType, extMsgData)) {
console.log('Unexpected message received: ' +
- message.data['type'] + ': ' + message.data['data']);
+ extMsgType + ': ' + extMsgData);
}
}
+
} else if (message.method == 'mediaSourceReset') {
- if (typeof(message.data['format']) != 'string') {
- console.error('Invalid mediaSourceReset message:', message.data);
- return;
- }
if (!this.mediaSourceRenderer_) {
console.error('Unexpected mediaSourceReset.');
return;
}
- this.mediaSourceRenderer_.reset(
- (/** @type {string} */ message.data['format']));
+ this.mediaSourceRenderer_.reset(getStringAttr(message.data, 'format'))
+
} else if (message.method == 'mediaSourceData') {
if (!(message.data['buffer'] instanceof ArrayBuffer)) {
console.error('Invalid mediaSourceData message:', message.data);
diff --git a/remoting/webapp/client_session.js b/remoting/webapp/client_session.js
index 3a78b4e..7a3f93a 100644
--- a/remoting/webapp/client_session.js
+++ b/remoting/webapp/client_session.js
@@ -214,6 +214,17 @@ remoting.ClientSession.State = {
FAILED: 5
};
+/**
+ * @param {string} state The state name.
+ * @return {remoting.ClientSession.State} The session state enum value.
+ */
+remoting.ClientSession.State.fromString = function(state) {
+ if (!remoting.ClientSession.State.hasOwnProperty(state)) {
+ throw "Invalid ClientSession.State: " + state;
+ }
+ return remoting.ClientSession.State[state];
+}
+
/** @enum {number} */
remoting.ClientSession.ConnectionError = {
UNKNOWN: -1,
@@ -225,6 +236,18 @@ remoting.ClientSession.ConnectionError = {
HOST_OVERLOAD: 5
};
+/**
+ * @param {string} error The connection error name.
+ * @return {remoting.ClientSession.ConnectionError} The connection error enum.
+ */
+remoting.ClientSession.ConnectionError.fromString = function(error) {
+ if (!remoting.ClientSession.ConnectionError.hasOwnProperty(error)) {
+ console.error('Unexpected ClientSession.ConnectionError string: ', error);
+ return remoting.ClientSession.ConnectionError.UNKNOWN;
+ }
+ return remoting.ClientSession.ConnectionError[error];
+}
+
// The mode of this session.
/** @enum {number} */
remoting.ClientSession.Mode = {
diff --git a/remoting/webapp/host_controller.js b/remoting/webapp/host_controller.js
index f780be7..caa1060 100644
--- a/remoting/webapp/host_controller.js
+++ b/remoting/webapp/host_controller.js
@@ -47,6 +47,17 @@ remoting.HostController.State = {
UNKNOWN: 6
};
+/**
+ * @param {string} state The host controller state name.
+ * @return {remoting.HostController.State} The state enum value.
+ */
+remoting.HostController.State.fromString = function(state) {
+ if (!remoting.HostController.State.hasOwnProperty(state)) {
+ throw "Invalid HostController.State: " + state;
+ }
+ return remoting.HostController.State[state];
+}
+
/** @enum {number} */
remoting.HostController.AsyncResult = {
OK: 0,
@@ -56,6 +67,17 @@ remoting.HostController.AsyncResult = {
};
/**
+ * @param {string} result The async result name.
+ * @return {remoting.HostController.AsyncResult} The result enum value.
+ */
+remoting.HostController.AsyncResult.fromString = function(result) {
+ if (!remoting.HostController.AsyncResult.hasOwnProperty(result)) {
+ throw "Invalid HostController.AsyncResult: " + result;
+ }
+ return remoting.HostController.AsyncResult[result];
+}
+
+/**
* Set of features for which hasFeature() can be used to test.
*
* @enum {string}
diff --git a/remoting/webapp/host_it2me_native_messaging.js b/remoting/webapp/host_it2me_native_messaging.js
index d62dc4f..45efaa9 100644
--- a/remoting/webapp/host_it2me_native_messaging.js
+++ b/remoting/webapp/host_it2me_native_messaging.js
@@ -146,107 +146,80 @@ remoting.HostIt2MeNativeMessaging.prototype.postMessage_ =
*/
remoting.HostIt2MeNativeMessaging.prototype.onIncomingMessage_ =
function(message) {
- /** @type {string} */
- var type = message['type'];
- if (!checkType_('type', type, 'string')) {
+ try {
+ this.handleIncomingMessage_(message);
+ } catch (e) {
+ console.error(/** @type {*} */ (e));
this.onError_(remoting.Error.UNEXPECTED);
- return;
}
+}
+
+/**
+ * Handler for incoming Native Messages.
+ *
+ * @param {Object} message The received message.
+ * @return {void}
+ * @private
+ */
+remoting.HostIt2MeNativeMessaging.prototype.handleIncomingMessage_ =
+ function(message) {
+ var type = getStringAttr(message, 'type');
switch (type) {
case 'helloResponse':
- /** @type {string} */
- var version = message['version'];
- if (checkType_('version', version, 'string')) {
- console.log('Host version: ', version);
- this.initialized_ = true;
- // A "hello" request is sent immediately after the native messaging host
- // is started. We can proceed to the next task once we receive the
- // "helloReponse".
- this.onHostStarted_();
- } else {
- this.onError_(remoting.Error.UNEXPECTED);
- }
+ var version = getStringAttr(message, 'version');
+ console.log('Host version: ', version);
+ this.initialized_ = true;
+ // A "hello" request is sent immediately after the native messaging host
+ // is started. We can proceed to the next task once we receive the
+ // "helloReponse".
+ this.onHostStarted_();
break;
case 'connectResponse':
console.log('connectResponse received');
// Response to the "connect" request. No action is needed until we
- // receive the corresponding "hostStateChagned" message.
+ // receive the corresponding "hostStateChanged" message.
break;
case 'disconnectResponse':
console.log('disconnectResponse received');
// Response to the "disconnect" request. No action is needed until we
- // receive the corresponding "hostStateChagned" message.
+ // receive the corresponding "hostStateChanged" message.
break;
case 'hostStateChanged':
- /** @type {string} */
- var stateString = message['state'];
- if (!checkType_('stateString', stateString, 'string')) {
- this.onError_(remoting.Error.UNEXPECTED);
- break;
- }
-
+ var stateString = getStringAttr(message, 'state');
console.log('hostStateChanged received: ', stateString);
-
- /** @type {remoting.HostSession.State} */
- var state = remoting.HostSession.stateFromString(stateString);
+ var state = remoting.HostSession.State.fromString(stateString);
switch (state) {
case remoting.HostSession.State.RECEIVED_ACCESS_CODE:
- /** @type {string} */
- var accessCode = message['accessCode'];
- if (!checkType_('accessCode', accessCode, 'string')) {
- this.onError_(remoting.Error.UNEXPECTED);
- break;
- }
-
- /** @type {number} */
- var accessCodeLifetime = message['accessCodeLifetime'];
- if (!checkType_('accessCodeLifetime', accessCodeLifetime, 'number')) {
- this.onError_(remoting.Error.UNEXPECTED);
- break;
- }
-
+ var accessCode = getStringAttr(message, 'accessCode');
+ var accessCodeLifetime = getNumberAttr(message, 'accessCodeLifetime');
this.onReceivedAccessCode_(accessCode, accessCodeLifetime);
break;
case remoting.HostSession.State.CONNECTED:
- /** @type {string} */
- var client = message['client'];
- if (checkType_('client', client, 'string')) {
- this.onConnected_(client);
- } else {
- this.onError_(remoting.Error.UNEXPECTED);
- }
+ var client = getStringAttr(message, 'client');
+ this.onConnected_(client);
break;
}
this.onStateChanged_(state);
break;
case 'natPolicyChanged':
- /** @type {boolean} */
- var natTraversalEnabled = message['natTraversalEnabled'];
- if (checkType_('natTraversalEnabled', natTraversalEnabled, 'boolean')) {
- this.onNatPolicyChanged_(natTraversalEnabled);
- } else {
- this.onError_(remoting.Error.UNEXPECTED);
- }
+ var natTraversalEnabled = getBooleanAttr(message, 'natTraversalEnabled');
+ this.onNatPolicyChanged_(natTraversalEnabled);
break;
case 'error':
- /** @type {string} */
- var description = message['description'];
- // Ignore the return value.
- checkType_('description', description, 'string');
+ console.error(getStringAttr(message, 'description'));
this.onError_(remoting.Error.UNEXPECTED);
break;
default:
- console.error('Unexpected native message: ', message);
- this.onError_(remoting.Error.UNEXPECTED);
+ throw 'Unexpected native message: ' + message;
}
};
diff --git a/remoting/webapp/host_native_messaging.js b/remoting/webapp/host_native_messaging.js
index 3fc8473..102c32e 100644
--- a/remoting/webapp/host_native_messaging.js
+++ b/remoting/webapp/host_native_messaging.js
@@ -42,7 +42,7 @@ remoting.HostNativeMessaging = function() {
* Type used for entries of |pendingReplies_| list.
*
* @param {string} type Type of the originating request.
- * @param {?function(...):void} onDone The callback, if any, to be triggered
+ * @param {function(...):void} onDone The callback, if any, to be triggered
* on response. The actual parameters depend on the original request type.
* @param {function(remoting.Error):void} onError The callback to be triggered
* on error.
@@ -81,59 +81,6 @@ remoting.HostNativeMessaging.prototype.initialize = function(onDone, onError) {
};
/**
- * Verifies that |object| is of type |type|, logging an error if not.
- *
- * @param {string} name Name of the object, to be included in the error log.
- * @param {*} object Object to test.
- * @param {string} type Expected type of the object.
- * @return {boolean} Result of test.
- */
-function checkType_(name, object, type) {
- if (typeof(object) !== type) {
- console.error('NativeMessaging: "' + name + '" expected to be of type "' +
- type + '", got: ' + object);
- return false;
- }
- return true;
-}
-
-/**
- * Returns |result| as an AsyncResult. If |result| is not valid, returns null
- * and logs an error.
- *
- * @param {*} result
- * @return {remoting.HostController.AsyncResult?} Converted result.
- */
-function asAsyncResult_(result) {
- if (!checkType_('result', result, 'string')) {
- return null;
- }
- if (!remoting.HostController.AsyncResult.hasOwnProperty(result)) {
- console.error('NativeMessaging: unexpected result code: ', result);
- return null;
- }
- return remoting.HostController.AsyncResult[result];
-}
-
-/**
- * Returns |result| as a HostController.State. If |result| is not valid,
- * returns null and logs an error.
- *
- * @param {*} result
- * @return {remoting.HostController.State?} Converted result.
- */
-function asHostState_(result) {
- if (!checkType_('result', result, 'string')) {
- return null;
- }
- if (!remoting.HostController.State.hasOwnProperty(result)) {
- console.error('NativeMessaging: unexpected result code: ', result);
- return null;
- }
- return remoting.HostController.State[result];
-}
-
-/**
* @param {remoting.HostController.Feature} feature The feature to test for.
* @return {boolean} True if the implementation supports the named feature.
*/
@@ -148,7 +95,7 @@ remoting.HostNativeMessaging.prototype.hasFeature = function(feature) {
* depending on the message type.
*
* @param {{type: string}} message The message to post.
- * @param {?function(...):void} onDone The callback, if any, to be triggered
+ * @param {function(...):void} onDone The callback, if any, to be triggered
* on response.
* @param {function(remoting.Error):void} onError The callback to be triggered
* on error.
@@ -185,126 +132,84 @@ remoting.HostNativeMessaging.prototype.onIncomingMessage_ = function(message) {
}
delete this.pendingReplies_[id];
- var onDone = reply.onDone;
- var onError = reply.onError;
-
- /** @type {string} */
- var type = message['type'];
- if (!checkType_('type', type, 'string')) {
- onError(remoting.Error.UNEXPECTED);
- return;
- }
- if (type != reply.type) {
- console.error('NativeMessaging: expected reply type: ', reply.type,
- ', got: ', type);
- onError(remoting.Error.UNEXPECTED);
- return;
+ try {
+ var type = getStringAttr(message, 'type');
+ if (type != reply.type) {
+ throw 'Expected reply type: ' + reply.type + ', got: ' + type;
+ }
+
+ this.handleIncomingMessage_(message, reply.onDone);
+ } catch (e) {
+ console.error('Error while processing native message' +
+ /** @type {*} */ (e));
+ reply.onError(remoting.Error.UNEXPECTED);
}
+}
+
+/**
+ * Handler for incoming Native Messages.
+ *
+ * @param {Object} message The received message.
+ * @param {function(...):void} onDone Function to call when we're done
+ * processing the message.
+ * @return {void} Nothing.
+ * @private
+ */
+remoting.HostNativeMessaging.prototype.handleIncomingMessage_ =
+ function(message, onDone) {
+ var type = getStringAttr(message, 'type');
switch (type) {
case 'helloResponse':
- /** @type {string} */
- var version = message['version'];
- if (checkType_('version', version, 'string')) {
- this.version_ = version;
- if (message['supportedFeatures'] instanceof Array) {
- this.supportedFeatures_ = message['supportedFeatures'];
- } else {
- // Old versions of the native messaging host do not return this list.
- // Those versions don't support any new feature.
- this.supportedFeatures_ = [];
- }
- onDone();
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ this.version_ = getStringAttr(message, 'version');
+ // Old versions of the native messaging host do not return this list.
+ // Those versions default to the empty list of supported features.
+ getArrayAttr(message, 'supportedFeatures', []);
+ onDone();
break;
case 'getHostNameResponse':
- /** @type {*} */
- var hostname = message['hostname'];
- if (checkType_('hostname', hostname, 'string')) {
- onDone(hostname);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ onDone(getStringAttr(message, 'hostname'));
break;
case 'getPinHashResponse':
- /** @type {*} */
- var hash = message['hash'];
- if (checkType_('hash', hash, 'string')) {
- onDone(hash);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ onDone(getStringAttr(message, 'hash'));
break;
case 'generateKeyPairResponse':
- /** @type {*} */
- var privateKey = message['privateKey'];
- /** @type {*} */
- var publicKey = message['publicKey'];
- if (checkType_('privateKey', privateKey, 'string') &&
- checkType_('publicKey', publicKey, 'string')) {
- onDone(privateKey, publicKey);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ var privateKey = getStringAttr(message, 'privateKey');
+ var publicKey = getStringAttr(message, 'publicKey');
+ onDone(privateKey, publicKey);
break;
case 'updateDaemonConfigResponse':
- var result = asAsyncResult_(message['result']);
- if (result != null) {
- onDone(result);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ var result = remoting.HostController.AsyncResult.fromString(
+ getStringAttr(message, 'result'));
+ onDone(result);
break;
case 'getDaemonConfigResponse':
- /** @type {*} */
- var config = message['config'];
- if (checkType_('config', config, 'object')) {
- onDone(config);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ onDone(getObjectAttr(message, 'config'));
break;
case 'getUsageStatsConsentResponse':
- /** @type {*} */
- var supported = message['supported'];
- /** @type {*} */
- var allowed = message['allowed'];
- /** @type {*} */
- var setByPolicy = message['setByPolicy'];
- if (checkType_('supported', supported, 'boolean') &&
- checkType_('allowed', allowed, 'boolean') &&
- checkType_('setByPolicy', setByPolicy, 'boolean')) {
- onDone(supported, allowed, setByPolicy);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ var supported = getBooleanAttr(message, 'supported');
+ var allowed = getBooleanAttr(message, 'allowed');
+ var setByPolicy = getBooleanAttr(message, 'setByPolicy');
+ onDone(supported, allowed, setByPolicy);
break;
case 'startDaemonResponse':
case 'stopDaemonResponse':
- var result = asAsyncResult_(message['result']);
- if (result != null) {
- onDone(result);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ var result = remoting.HostController.AsyncResult.fromString(
+ getStringAttr(message, 'result'));
+ onDone(result);
break;
case 'getDaemonStateResponse':
- var state = asHostState_(message['state']);
- if (state != null) {
- onDone(state);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ var state = remoting.HostController.State.fromString(
+ getStringAttr(message, 'state'));
+ onDone(state);
break;
case 'getPairedClientsResponse':
@@ -313,47 +218,31 @@ remoting.HostNativeMessaging.prototype.onIncomingMessage_ = function(message) {
if (pairedClients != null) {
onDone(pairedClients);
} else {
- onError(remoting.Error.UNEXPECTED);
+ throw 'No paired clients!';
}
break;
case 'clearPairedClientsResponse':
case 'deletePairedClientResponse':
- /** @type {boolean} */
- var success = message['result'];
- if (checkType_('success', success, 'boolean')) {
- onDone(success);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ onDone(getBooleanAttr(message, 'result'));
break;
case 'getHostClientIdResponse':
- /** @type {string} */
- var clientId = message['clientId'];
- if (checkType_('clientId', clientId, 'string')) {
- onDone(clientId);
- } else {
- onError(remoting.Error.UNEXPECTED);
- }
+ onDone(getStringAttr(message, 'clientId'));
break;
case 'getCredentialsFromAuthCodeResponse':
- /** @type {string} */
- var userEmail = message['userEmail'];
- /** @type {string} */
- var refreshToken = message['refreshToken'];
- if (checkType_('userEmail', userEmail, 'string') && userEmail &&
- checkType_('refreshToken', refreshToken, 'string') && refreshToken) {
+ var userEmail = getStringAttr(message, 'userEmail');
+ var refreshToken = getStringAttr(message, 'refreshToken');
+ if (userEmail && refreshToken) {
onDone(userEmail, refreshToken);
} else {
- onError(remoting.Error.UNEXPECTED);
+ throw 'Missing userEmail or refreshToken';
}
break;
default:
- console.error('Unexpected native message: ', message);
- onError(remoting.Error.UNEXPECTED);
+ throw 'Unexpected native message: ' + message;
}
};
diff --git a/remoting/webapp/host_session.js b/remoting/webapp/host_session.js
index bf6204f..4aa70cb 100644
--- a/remoting/webapp/host_session.js
+++ b/remoting/webapp/host_session.js
@@ -44,9 +44,9 @@ remoting.HostSession.State = {
* @return {remoting.HostSession.State} The HostSession.State enum value
* corresponding to stateString.
*/
-remoting.HostSession.stateFromString = function(stateString) {
+remoting.HostSession.State.fromString = function(stateString) {
if (!remoting.HostSession.State.hasOwnProperty(stateString)) {
- console.error('NativeMessaging: unexpected state string: ', stateString);
+ console.error('Unexpected HostSession.State string: ', stateString);
return remoting.HostSession.State.UNKNOWN;
}
return remoting.HostSession.State[stateString];
diff --git a/remoting/webapp/main.html b/remoting/webapp/main.html
index 9106c2d..32ff10a 100644
--- a/remoting/webapp/main.html
+++ b/remoting/webapp/main.html
@@ -52,6 +52,7 @@ found in the LICENSE file.
<script src="third_party_host_permissions.js"></script>
<script src="third_party_token_fetcher.js"></script>
<script src="toolbar.js"></script>
+ <script src="typecheck.js"></script>
<script src="ui_mode.js"></script>
<script src="xhr.js"></script>
<script src="wcs_sandbox_container.js"></script>
diff --git a/remoting/webapp/typecheck.js b/remoting/webapp/typecheck.js
new file mode 100644
index 0000000..4da66f7
--- /dev/null
+++ b/remoting/webapp/typecheck.js
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Get the |key| attribute in the given |dict| and verify that it is an
+ * array value.
+ *
+ * If the attribute is not an array, then an exception will be thrown unless
+ * a default value is specified in |opt_default|.
+ *
+ * @param {Object.<string,*>} dict The dictionary containing the |key|
+ * @param {string} key The key to typecheck in the |dict|.
+ * @param {Array=} opt_default The value to return if the key is not a bool.
+ * @return {Array} The |key| attribute value as an object.
+ */
+function getArrayAttr(dict, key, opt_default) {
+ var value = /** @type {Array} */ (dict[key]);
+ if (!(value instanceof Array)) {
+ if (opt_default === undefined) {
+ throw 'Invalid data type for ' + key +
+ ' (expected: array, actual: ' + typeof value + ')';
+ } else {
+ return opt_default;
+ }
+ }
+ return value;
+}
+
+/**
+ * Get the |key| attribute in the given |dict| and verify that it is a
+ * boolean value.
+ *
+ * If the attribute is not a boolean, then an exception will be thrown unless
+ * a default value is specified in |opt_default|.
+ *
+ * @param {Object.<string,*>} dict The dictionary containing the |key|
+ * @param {string} key The key to typecheck in the |dict|.
+ * @param {boolean=} opt_default The value to return if the key is not a bool.
+ * @return {boolean} The |key| attribute value as a boolean.
+ */
+function getBooleanAttr(dict, key, opt_default) {
+ var value = /** @type {boolean} */ (dict[key]);
+ if (typeof value != 'boolean') {
+ if (opt_default === undefined) {
+ throw 'Invalid data type for ' + key +
+ ' (expected: boolean, actual: ' + typeof value + ')';
+ } else {
+ return opt_default;
+ }
+ }
+ return value;
+}
+
+/**
+ * Get the |key| attribute in the given |dict| and verify that it is a
+ * number value.
+ *
+ * If the attribute is not a number, then an exception will be thrown unless
+ * a default value is specified in |opt_default|.
+ *
+ * @param {Object.<string,*>} dict The dictionary containing the |key|
+ * @param {string} key The key to typecheck in the |dict|.
+ * @param {number=} opt_default The value to return if the key is not a number.
+ * @return {number} The |key| attribute value as a number.
+ */
+function getNumberAttr(dict, key, opt_default) {
+ var value = /** @type {number} */(dict[key]);
+ if (typeof value != 'number') {
+ if (opt_default === undefined) {
+ throw 'Invalid data type for ' + key +
+ ' (expected: number, actual: ' + typeof value + ')';
+ } else {
+ return opt_default;
+ }
+ }
+ return value;
+}
+
+/**
+ * Get the |key| attribute in the given |dict| and verify that it is an
+ * object value.
+ *
+ * If the attribute is not an object, then an exception will be thrown unless
+ * a default value is specified in |opt_default|.
+ *
+ * @param {Object.<string,*>} dict The dictionary containing the |key|
+ * @param {string} key The key to typecheck in the |dict|.
+ * @param {Object=} opt_default The value to return if the key is not a bool.
+ * @return {Object} The |key| attribute value as an object.
+ */
+function getObjectAttr(dict, key, opt_default) {
+ var value = /** @type {Object} */ (dict[key]);
+ if (typeof value != 'object') {
+ if (opt_default === undefined) {
+ throw 'Invalid data type for ' + key +
+ ' (expected: object, actual: ' + typeof value + ')';
+ } else {
+ return opt_default;
+ }
+ }
+ return value;
+}
+
+/**
+ * Get the |key| attribute in the given |dict| and verify that it is a
+ * string value.
+ *
+ * If the attribute is not a string, then an exception will be thrown unless
+ * a default value is specified in |opt_default|.
+ *
+ * @param {Object.<string,*>} dict The dictionary containing the |key|
+ * @param {string} key The key to typecheck in the |dict|.
+ * @param {string=} opt_default The value to return if the key is not a string.
+ * @return {string} The |key| attribute value as a string.
+ */
+function getStringAttr(dict, key, opt_default) {
+ var value = /** @type {string} */ (dict[key]);
+ if (typeof value != 'string') {
+ if (opt_default === undefined) {
+ throw 'Invalid data type for ' + key +
+ ' (expected: string, actual: ' + typeof value + ')';
+ } else {
+ return opt_default;
+ }
+ }
+ return value;
+}