diff options
author | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-12 22:50:38 +0000 |
---|---|---|
committer | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-12 22:50:38 +0000 |
commit | ef2f63388a322916314c332f5113b2fef9066789 (patch) | |
tree | d9b895558678c79870ac80cc1a753cdb59169f82 /remoting/webapp | |
parent | 0b024473aa7be95e830f9c9c971d9745f9004aa4 (diff) | |
download | chromium_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.js | 211 | ||||
-rw-r--r-- | remoting/webapp/client_session.js | 23 | ||||
-rw-r--r-- | remoting/webapp/host_controller.js | 22 | ||||
-rw-r--r-- | remoting/webapp/host_it2me_native_messaging.js | 97 | ||||
-rw-r--r-- | remoting/webapp/host_native_messaging.js | 229 | ||||
-rw-r--r-- | remoting/webapp/host_session.js | 4 | ||||
-rw-r--r-- | remoting/webapp/main.html | 1 | ||||
-rw-r--r-- | remoting/webapp/typecheck.js | 128 |
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; +} |