Commit b814ba0d authored by jrw's avatar jrw Committed by Commit bot

Added more typechecking functions and unit tests for existing code.

This change alters the functionality of getObjectAttr slightly: it no
longer accepts null or Array objects as "object" values.  I made this
change because the type checking code seems designed to mirror the
semantics of JSON, where each value belongs to exactly one of the
following categories: array, boolean, number, null, object, or string.

This change also changes the type of values thrown from strings to Error objects so that there are more likely to be useful stack traces when an error occurs.

BUG=

Review URL: https://codereview.chromium.org/1015553003

Cr-Commit-Position: refs/heads/master@{#321415}
parent 43dc9613
...@@ -77,6 +77,7 @@ ...@@ -77,6 +77,7 @@
'webapp/crd/js/identity_unittest.js', 'webapp/crd/js/identity_unittest.js',
'webapp/crd/js/l10n_unittest.js', 'webapp/crd/js/l10n_unittest.js',
'webapp/crd/js/menu_button_unittest.js', 'webapp/crd/js/menu_button_unittest.js',
'webapp/crd/js/typecheck_unittest.js',
'webapp/crd/js/xhr_unittest.js', 'webapp/crd/js/xhr_unittest.js',
'webapp/crd/js/xmpp_connection_unittest.js', 'webapp/crd/js/xmpp_connection_unittest.js',
'webapp/crd/js/xmpp_login_handler_unittest.js', 'webapp/crd/js/xmpp_login_handler_unittest.js',
......
...@@ -315,7 +315,7 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { ...@@ -315,7 +315,7 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) {
case 'openURL': case 'openURL':
// URL requests from the hosted app are untrusted, so disallow anything // URL requests from the hosted app are untrusted, so disallow anything
// other than HTTP or HTTPS. // other than HTTP or HTTPS.
var url = getStringAttr(message, 'url'); var url = base.getStringAttr(message, 'url');
if (url.indexOf('http:') != 0 && url.indexOf('https:') != 0) { if (url.indexOf('http:') != 0 && url.indexOf('https:') != 0) {
console.error('Bad URL: ' + url); console.error('Bad URL: ' + url);
} else { } else {
...@@ -324,13 +324,13 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { ...@@ -324,13 +324,13 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) {
return true; return true;
case 'onWindowRemoved': case 'onWindowRemoved':
var id = getNumberAttr(message, 'id'); var id = base.getNumberAttr(message, 'id');
this.windowActivationMenu_.remove(id); this.windowActivationMenu_.remove(id);
return true; return true;
case 'onWindowAdded': case 'onWindowAdded':
var id = getNumberAttr(message, 'id'); var id = base.getNumberAttr(message, 'id');
var title = getStringAttr(message, 'title'); var title = base.getStringAttr(message, 'title');
this.windowActivationMenu_.add(id, title); this.windowActivationMenu_.add(id, title);
return true; return true;
...@@ -339,15 +339,15 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { ...@@ -339,15 +339,15 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) {
return true; return true;
case 'setKeyboardLayouts': case 'setKeyboardLayouts':
var supportedLayouts = getArrayAttr(message, 'supportedLayouts'); var supportedLayouts = base.getArrayAttr(message, 'supportedLayouts');
var currentLayout = getStringAttr(message, 'currentLayout'); var currentLayout = base.getStringAttr(message, 'currentLayout');
console.log('Current host keyboard layout: ' + currentLayout); console.log('Current host keyboard layout: ' + currentLayout);
console.log('Supported host keyboard layouts: ' + supportedLayouts); console.log('Supported host keyboard layouts: ' + supportedLayouts);
this.keyboardLayoutsMenu_.setLayouts(supportedLayouts, currentLayout); this.keyboardLayoutsMenu_.setLayouts(supportedLayouts, currentLayout);
return true; return true;
case 'pingResponse': case 'pingResponse':
var then = getNumberAttr(message, 'timestamp'); var then = base.getNumberAttr(message, 'timestamp');
var now = new Date().getTime(); var now = new Date().getTime();
this.contextMenu_.updateConnectionRTT(now - then); this.contextMenu_.updateConnectionRTT(now - then);
return true; return true;
......
...@@ -34,7 +34,6 @@ base.debug.assert = function(expr, opt_msg) { ...@@ -34,7 +34,6 @@ base.debug.assert = function(expr, opt_msg) {
if (opt_msg) { if (opt_msg) {
msg += ' ' + opt_msg; msg += ' ' + opt_msg;
} }
console.error(msg);
if (base.debug.breakOnAssert) { if (base.debug.breakOnAssert) {
alert(msg); alert(msg);
debugger; debugger;
......
...@@ -251,7 +251,7 @@ remoting.CastExtensionHandler.prototype.chromotingMessageListener = ...@@ -251,7 +251,7 @@ remoting.CastExtensionHandler.prototype.chromotingMessageListener =
function(ns, message) { function(ns, message) {
if (ns === this.kCastNamespace_) { if (ns === this.kCastNamespace_) {
try { try {
var messageObj = getJsonObjectFromString(message); var messageObj = base.getJsonObjectFromString(message);
this.sendMessageToHost_(messageObj); this.sendMessageToHost_(messageObj);
} catch (err) { } catch (err) {
console.error('Failed to process message from Cast device.'); console.error('Failed to process message from Cast device.');
......
...@@ -86,10 +86,10 @@ remoting.ClientPlugin.HostDesktopImpl.prototype.resize = function( ...@@ -86,10 +86,10 @@ remoting.ClientPlugin.HostDesktopImpl.prototype.resize = function(
*/ */
remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function( remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function(
message) { message) {
this.width_ = getNumberAttr(message.data, 'width'); this.width_ = base.getNumberAttr(message.data, 'width');
this.height_ = getNumberAttr(message.data, 'height'); this.height_ = base.getNumberAttr(message.data, 'height');
this.xDpi_ = getNumberAttr(message.data, 'x_dpi', 96); this.xDpi_ = base.getNumberAttr(message.data, 'x_dpi', 96);
this.yDpi_ = getNumberAttr(message.data, 'y_dpi', 96); this.yDpi_ = base.getNumberAttr(message.data, 'y_dpi', 96);
this.raiseEvent(remoting.HostDesktop.Events.sizeChanged, this.raiseEvent(remoting.HostDesktop.Events.sizeChanged,
this.getDimensions()); this.getDimensions());
}; };
...@@ -104,7 +104,7 @@ remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function( ...@@ -104,7 +104,7 @@ remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function(
*/ */
remoting.ClientPlugin.HostDesktopImpl.prototype.onShapeUpdated = remoting.ClientPlugin.HostDesktopImpl.prototype.onShapeUpdated =
function(message) { function(message) {
var shapes = getArrayAttr(message.data, 'rects'); var shapes = base.getArrayAttr(message.data, 'rects');
var rects = shapes.map( var rects = shapes.map(
/** @param {Array.<number>} shape */ /** @param {Array.<number>} shape */
function(shape) { function(shape) {
......
...@@ -212,35 +212,36 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { ...@@ -212,35 +212,36 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) {
var handler = this.connectionEventHandler_; var handler = this.connectionEventHandler_;
if (message.method == 'sendOutgoingIq') { if (message.method == 'sendOutgoingIq') {
handler.onOutgoingIq(getStringAttr(message.data, 'iq')); handler.onOutgoingIq(base.getStringAttr(message.data, 'iq'));
} else if (message.method == 'logDebugMessage') { } else if (message.method == 'logDebugMessage') {
handler.onDebugMessage(getStringAttr(message.data, 'message')); handler.onDebugMessage(base.getStringAttr(message.data, 'message'));
} else if (message.method == 'onConnectionStatus') { } else if (message.method == 'onConnectionStatus') {
var state = remoting.ClientSession.State.fromString( var state = remoting.ClientSession.State.fromString(
getStringAttr(message.data, 'state')); base.getStringAttr(message.data, 'state'));
var error = remoting.ClientSession.ConnectionError.fromString( var error = remoting.ClientSession.ConnectionError.fromString(
getStringAttr(message.data, 'error')); base.getStringAttr(message.data, 'error'));
handler.onConnectionStatusUpdate(state, error); handler.onConnectionStatusUpdate(state, error);
} else if (message.method == 'onRouteChanged') { } else if (message.method == 'onRouteChanged') {
var channel = getStringAttr(message.data, 'channel'); var channel = base.getStringAttr(message.data, 'channel');
var connectionType = getStringAttr(message.data, 'connectionType'); var connectionType = base.getStringAttr(message.data, 'connectionType');
handler.onRouteChanged(channel, connectionType); handler.onRouteChanged(channel, connectionType);
} else if (message.method == 'onConnectionReady') { } else if (message.method == 'onConnectionReady') {
var ready = getBooleanAttr(message.data, 'ready'); var ready = base.getBooleanAttr(message.data, 'ready');
handler.onConnectionReady(ready); handler.onConnectionReady(ready);
} else if (message.method == 'setCapabilities') { } else if (message.method == 'setCapabilities') {
/** @type {!Array<string>} */ /** @type {!Array<string>} */
var capabilities = tokenize(getStringAttr(message.data, 'capabilities')); var capabilities = tokenize(
base.getStringAttr(message.data, 'capabilities'));
handler.onSetCapabilities(capabilities); handler.onSetCapabilities(capabilities);
} else if (message.method == 'extensionMessage') { } else if (message.method == 'extensionMessage') {
var extMsgType = getStringAttr(message.data, 'type'); var extMsgType = base.getStringAttr(message.data, 'type');
var extMsgData = getStringAttr(message.data, 'data'); var extMsgData = base.getStringAttr(message.data, 'data');
handler.onExtensionMessage(extMsgType, extMsgData); handler.onExtensionMessage(extMsgType, extMsgData);
} }
} }
...@@ -248,19 +249,20 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { ...@@ -248,19 +249,20 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) {
if (message.method == 'hello') { if (message.method == 'hello') {
// Resize in case we had to enlarge it to support click-to-play. // Resize in case we had to enlarge it to support click-to-play.
this.hidePluginForClickToPlay_(); this.hidePluginForClickToPlay_();
this.pluginApiVersion_ = getNumberAttr(message.data, 'apiVersion'); this.pluginApiVersion_ = base.getNumberAttr(message.data, 'apiVersion');
this.pluginApiMinVersion_ = getNumberAttr(message.data, 'apiMinVersion'); this.pluginApiMinVersion_ =
base.getNumberAttr(message.data, 'apiMinVersion');
if (this.pluginApiVersion_ >= 7) { if (this.pluginApiVersion_ >= 7) {
this.pluginApiFeatures_ = this.pluginApiFeatures_ =
tokenize(getStringAttr(message.data, 'apiFeatures')); tokenize(base.getStringAttr(message.data, 'apiFeatures'));
// Negotiate capabilities. // Negotiate capabilities.
/** @type {!Array<string>} */ /** @type {!Array<string>} */
var supportedCapabilities = []; var supportedCapabilities = [];
if ('supportedCapabilities' in message.data) { if ('supportedCapabilities' in message.data) {
supportedCapabilities = supportedCapabilities =
tokenize(getStringAttr(message.data, 'supportedCapabilities')); tokenize(base.getStringAttr(message.data, 'supportedCapabilities'));
} }
// At the moment the webapp does not recognize any of // At the moment the webapp does not recognize any of
// 'requestedCapabilities' capabilities (so they all should be disabled) // 'requestedCapabilities' capabilities (so they all should be disabled)
...@@ -287,19 +289,19 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { ...@@ -287,19 +289,19 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) {
} else if (message.method == 'onPerfStats') { } else if (message.method == 'onPerfStats') {
// Return value is ignored. These calls will throw an error if the value // Return value is ignored. These calls will throw an error if the value
// is not a number. // is not a number.
getNumberAttr(message.data, 'videoBandwidth'); base.getNumberAttr(message.data, 'videoBandwidth');
getNumberAttr(message.data, 'videoFrameRate'); base.getNumberAttr(message.data, 'videoFrameRate');
getNumberAttr(message.data, 'captureLatency'); base.getNumberAttr(message.data, 'captureLatency');
getNumberAttr(message.data, 'encodeLatency'); base.getNumberAttr(message.data, 'encodeLatency');
getNumberAttr(message.data, 'decodeLatency'); base.getNumberAttr(message.data, 'decodeLatency');
getNumberAttr(message.data, 'renderLatency'); base.getNumberAttr(message.data, 'renderLatency');
getNumberAttr(message.data, 'roundtripLatency'); base.getNumberAttr(message.data, 'roundtripLatency');
this.perfStats_ = this.perfStats_ =
/** @type {remoting.ClientSession.PerfStats} */ (message.data); /** @type {remoting.ClientSession.PerfStats} */ (message.data);
} else if (message.method == 'injectClipboardItem') { } else if (message.method == 'injectClipboardItem') {
var mimetype = getStringAttr(message.data, 'mimeType'); var mimetype = base.getStringAttr(message.data, 'mimeType');
var item = getStringAttr(message.data, 'item'); var item = base.getStringAttr(message.data, 'item');
if (remoting.clipboard) { if (remoting.clipboard) {
remoting.clipboard.fromHost(mimetype, item); remoting.clipboard.fromHost(mimetype, item);
} }
...@@ -313,33 +315,33 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { ...@@ -313,33 +315,33 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) {
// The pairingSupported value in the dictionary indicates whether both // The pairingSupported value in the dictionary indicates whether both
// client and host support pairing. If the client doesn't support pairing, // 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. // then the value won't be there at all, so give it a default of false.
var pairingSupported = getBooleanAttr(message.data, 'pairingSupported', var pairingSupported = base.getBooleanAttr(message.data, 'pairingSupported',
false); false);
this.credentials_.getPIN(pairingSupported).then( this.credentials_.getPIN(pairingSupported).then(
this.onPinFetched_.bind(this) this.onPinFetched_.bind(this)
); );
} else if (message.method == 'fetchThirdPartyToken') { } else if (message.method == 'fetchThirdPartyToken') {
var tokenUrl = getStringAttr(message.data, 'tokenUrl'); var tokenUrl = base.getStringAttr(message.data, 'tokenUrl');
var hostPublicKey = getStringAttr(message.data, 'hostPublicKey'); var hostPublicKey = base.getStringAttr(message.data, 'hostPublicKey');
var scope = getStringAttr(message.data, 'scope'); var scope = base.getStringAttr(message.data, 'scope');
this.credentials_.getThirdPartyToken(tokenUrl, hostPublicKey, scope).then( this.credentials_.getThirdPartyToken(tokenUrl, hostPublicKey, scope).then(
this.onThirdPartyTokenFetched_.bind(this) this.onThirdPartyTokenFetched_.bind(this)
); );
} else if (message.method == 'pairingResponse') { } else if (message.method == 'pairingResponse') {
var clientId = getStringAttr(message.data, 'clientId'); var clientId = base.getStringAttr(message.data, 'clientId');
var sharedSecret = getStringAttr(message.data, 'sharedSecret'); var sharedSecret = base.getStringAttr(message.data, 'sharedSecret');
this.onPairingComplete_(clientId, sharedSecret); this.onPairingComplete_(clientId, sharedSecret);
} else if (message.method == 'unsetCursorShape') { } else if (message.method == 'unsetCursorShape') {
this.updateMouseCursorImage_('', 0, 0); this.updateMouseCursorImage_('', 0, 0);
} else if (message.method == 'setCursorShape') { } else if (message.method == 'setCursorShape') {
var width = getNumberAttr(message.data, 'width'); var width = base.getNumberAttr(message.data, 'width');
var height = getNumberAttr(message.data, 'height'); var height = base.getNumberAttr(message.data, 'height');
var hotspotX = getNumberAttr(message.data, 'hotspotX'); var hotspotX = base.getNumberAttr(message.data, 'hotspotX');
var hotspotY = getNumberAttr(message.data, 'hotspotY'); var hotspotY = base.getNumberAttr(message.data, 'hotspotY');
var srcArrayBuffer = getObjectAttr(message.data, 'data'); var srcArrayBuffer = base.getObjectAttr(message.data, 'data');
var canvas = var canvas =
/** @type {HTMLCanvasElement} */ (document.createElement('canvas')); /** @type {HTMLCanvasElement} */ (document.createElement('canvas'));
......
...@@ -62,12 +62,12 @@ remoting.GnubbyAuthHandler.prototype.sendMessageToHost_ = function(data) { ...@@ -62,12 +62,12 @@ remoting.GnubbyAuthHandler.prototype.sendMessageToHost_ = function(data) {
*/ */
remoting.GnubbyAuthHandler.prototype.onExtensionMessage = remoting.GnubbyAuthHandler.prototype.onExtensionMessage =
function(type, message) { function(type, message) {
var messageType = getStringAttr(message, 'type'); var messageType = base.getStringAttr(message, 'type');
if (messageType == 'data') { if (messageType == 'data') {
this.sendMessageToGnubbyd_({ this.sendMessageToGnubbyd_({
'type': 'auth-agent@openssh.com', 'type': 'auth-agent@openssh.com',
'data': getArrayAttr(message, 'data') 'data': base.getArrayAttr(message, 'data')
}, this.callback_.bind(this, getNumberAttr(message, 'connectionId'))); }, this.callback_.bind(this, base.getNumberAttr(message, 'connectionId')));
} else { } else {
console.error('Invalid gnubby-auth message: ' + messageType); console.error('Invalid gnubby-auth message: ' + messageType);
return false; return false;
...@@ -87,7 +87,7 @@ remoting.GnubbyAuthHandler.prototype.callback_ = ...@@ -87,7 +87,7 @@ remoting.GnubbyAuthHandler.prototype.callback_ =
this.sendMessageToHost_({ this.sendMessageToHost_({
'type': 'data', 'type': 'data',
'connectionId': connectionId, 'connectionId': connectionId,
'data': getArrayAttr(response, 'data') 'data': base.getArrayAttr(response, 'data')
}); });
} catch (/** @type {*} */ err) { } catch (/** @type {*} */ err) {
console.error('gnubby callback failed: ', err); console.error('gnubby callback failed: ', err);
......
...@@ -86,13 +86,13 @@ remoting.Host.Options.prototype.load = function() { ...@@ -86,13 +86,13 @@ remoting.Host.Options.prototype.load = function() {
// TODO(kelvinp): Uses a separate host options for app-remoting that // TODO(kelvinp): Uses a separate host options for app-remoting that
// hardcodes resizeToClient to true. // hardcodes resizeToClient to true.
that.resizeToClient = that.resizeToClient =
getBooleanAttr(options, 'resizeToClient', true); base.getBooleanAttr(options, 'resizeToClient', true);
that.shrinkToFit = getBooleanAttr(options, 'shrinkToFit', true); that.shrinkToFit = base.getBooleanAttr(options, 'shrinkToFit', true);
that.desktopScale = getNumberAttr(options, 'desktopScale', 1); that.desktopScale = base.getNumberAttr(options, 'desktopScale', 1);
that.remapKeys = getStringAttr(options, 'remapKeys', ''); that.remapKeys = base.getStringAttr(options, 'remapKeys', '');
that.pairingInfo = that.pairingInfo =
/** @type {remoting.PairingInfo} */ ( /** @type {remoting.PairingInfo} */ (
getObjectAttr(options, 'pairingInfo', that.pairingInfo)); base.getObjectAttr(options, 'pairingInfo', that.pairingInfo));
}); });
}; };
......
...@@ -199,7 +199,7 @@ remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) { ...@@ -199,7 +199,7 @@ remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) {
delete this.pendingReplies_[id]; delete this.pendingReplies_[id];
try { try {
var type = getStringAttr(message, 'type'); var type = base.getStringAttr(message, 'type');
if (type != reply.type) { if (type != reply.type) {
throw 'Expected reply type: ' + reply.type + ', got: ' + type; throw 'Expected reply type: ' + reply.type + ', got: ' + type;
} }
...@@ -222,58 +222,59 @@ remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) { ...@@ -222,58 +222,59 @@ remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) {
*/ */
remoting.HostDaemonFacade.prototype.handleIncomingMessage_ = remoting.HostDaemonFacade.prototype.handleIncomingMessage_ =
function(message, onDone) { function(message, onDone) {
var type = getStringAttr(message, 'type'); var type = base.getStringAttr(message, 'type');
switch (type) { switch (type) {
case 'helloResponse': case 'helloResponse':
this.version_ = getStringAttr(message, 'version'); this.version_ = base.getStringAttr(message, 'version');
// Old versions of the native messaging host do not return this list. // Old versions of the native messaging host do not return this list.
// Those versions default to the empty list of supported features. // Those versions default to the empty list of supported features.
this.supportedFeatures_ = getArrayAttr(message, 'supportedFeatures', []); this.supportedFeatures_ =
base.getArrayAttr(message, 'supportedFeatures', []);
onDone(); onDone();
break; break;
case 'getHostNameResponse': case 'getHostNameResponse':
onDone(getStringAttr(message, 'hostname')); onDone(base.getStringAttr(message, 'hostname'));
break; break;
case 'getPinHashResponse': case 'getPinHashResponse':
onDone(getStringAttr(message, 'hash')); onDone(base.getStringAttr(message, 'hash'));
break; break;
case 'generateKeyPairResponse': case 'generateKeyPairResponse':
var privateKey = getStringAttr(message, 'privateKey'); var privateKey = base.getStringAttr(message, 'privateKey');
var publicKey = getStringAttr(message, 'publicKey'); var publicKey = base.getStringAttr(message, 'publicKey');
onDone(privateKey, publicKey); onDone(privateKey, publicKey);
break; break;
case 'updateDaemonConfigResponse': case 'updateDaemonConfigResponse':
var result = remoting.HostController.AsyncResult.fromString( var result = remoting.HostController.AsyncResult.fromString(
getStringAttr(message, 'result')); base.getStringAttr(message, 'result'));
onDone(result); onDone(result);
break; break;
case 'getDaemonConfigResponse': case 'getDaemonConfigResponse':
onDone(getObjectAttr(message, 'config')); onDone(base.getObjectAttr(message, 'config'));
break; break;
case 'getUsageStatsConsentResponse': case 'getUsageStatsConsentResponse':
var supported = getBooleanAttr(message, 'supported'); var supported = base.getBooleanAttr(message, 'supported');
var allowed = getBooleanAttr(message, 'allowed'); var allowed = base.getBooleanAttr(message, 'allowed');
var setByPolicy = getBooleanAttr(message, 'setByPolicy'); var setByPolicy = base.getBooleanAttr(message, 'setByPolicy');
onDone(supported, allowed, setByPolicy); onDone(supported, allowed, setByPolicy);
break; break;
case 'startDaemonResponse': case 'startDaemonResponse':
case 'stopDaemonResponse': case 'stopDaemonResponse':
var result = remoting.HostController.AsyncResult.fromString( var result = remoting.HostController.AsyncResult.fromString(
getStringAttr(message, 'result')); base.getStringAttr(message, 'result'));
onDone(result); onDone(result);
break; break;
case 'getDaemonStateResponse': case 'getDaemonStateResponse':
var state = remoting.HostController.State.fromString( var state = remoting.HostController.State.fromString(
getStringAttr(message, 'state')); base.getStringAttr(message, 'state'));
onDone(state); onDone(state);
break; break;
...@@ -289,16 +290,16 @@ remoting.HostDaemonFacade.prototype.handleIncomingMessage_ = ...@@ -289,16 +290,16 @@ remoting.HostDaemonFacade.prototype.handleIncomingMessage_ =
case 'clearPairedClientsResponse': case 'clearPairedClientsResponse':
case 'deletePairedClientResponse': case 'deletePairedClientResponse':
onDone(getBooleanAttr(message, 'result')); onDone(base.getBooleanAttr(message, 'result'));
break; break;
case 'getHostClientIdResponse': case 'getHostClientIdResponse':
onDone(getStringAttr(message, 'clientId')); onDone(base.getStringAttr(message, 'clientId'));
break; break;
case 'getCredentialsFromAuthCodeResponse': case 'getCredentialsFromAuthCodeResponse':
var userEmail = getStringAttr(message, 'userEmail'); var userEmail = base.getStringAttr(message, 'userEmail');
var refreshToken = getStringAttr(message, 'refreshToken'); var refreshToken = base.getStringAttr(message, 'refreshToken');
if (userEmail && refreshToken) { if (userEmail && refreshToken) {
onDone(userEmail, refreshToken); onDone(userEmail, refreshToken);
} else { } else {
......
...@@ -118,15 +118,17 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ = ...@@ -118,15 +118,17 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ =
var hosts = items.map( var hosts = items.map(
function(/** Object */ item) { function(/** Object */ item) {
var host = new remoting.Host(); var host = new remoting.Host();
host.hostName = getStringAttr(item, 'hostName', ''); host.hostName = base.getStringAttr(item, 'hostName', '');
host.hostId = getStringAttr(item, 'hostId', ''); host.hostId = base.getStringAttr(item, 'hostId', '');
host.status = getStringAttr(item, 'status', ''); host.status = base.getStringAttr(item, 'status', '');
host.jabberId = getStringAttr(item, 'jabberId', ''); host.jabberId = base.getStringAttr(item, 'jabberId', '');
host.publicKey = getStringAttr(item, 'publicKey', ''); host.publicKey = base.getStringAttr(item, 'publicKey', '');
host.hostVersion = getStringAttr(item, 'hostVersion', ''); host.hostVersion = base.getStringAttr(item, 'hostVersion', '');
host.tokenUrlPatterns = getArrayAttr(item, 'tokenUrlPatterns', []); host.tokenUrlPatterns =
host.updatedTime = getStringAttr(item, 'updatedTime', ''); base.getArrayAttr(item, 'tokenUrlPatterns', []);
host.hostOfflineReason = getStringAttr(item, 'hostOfflineReason', ''); host.updatedTime = base.getStringAttr(item, 'updatedTime', '');
host.hostOfflineReason =
base.getStringAttr(item, 'hostOfflineReason', '');
return host; return host;
}); });
onDone(hosts); onDone(hosts);
......
...@@ -208,11 +208,11 @@ remoting.It2MeHostFacade.prototype.getClient = function() { ...@@ -208,11 +208,11 @@ remoting.It2MeHostFacade.prototype.getClient = function() {
*/ */
remoting.It2MeHostFacade.prototype.onIncomingMessage_ = remoting.It2MeHostFacade.prototype.onIncomingMessage_ =
function(message) { function(message) {
var type = getStringAttr(message, 'type'); var type = base.getStringAttr(message, 'type');
switch (type) { switch (type) {
case 'helloResponse': case 'helloResponse':
var version = getStringAttr(message, 'version'); var version = base.getStringAttr(message, 'version');
console.log('Host version: ', version); console.log('Host version: ', version);
this.initialized_ = true; this.initialized_ = true;
// A "hello" request is sent immediately after the native messaging host // A "hello" request is sent immediately after the native messaging host
...@@ -236,19 +236,20 @@ remoting.It2MeHostFacade.prototype.onIncomingMessage_ = ...@@ -236,19 +236,20 @@ remoting.It2MeHostFacade.prototype.onIncomingMessage_ =
break; break;
case 'hostStateChanged': case 'hostStateChanged':
var stateString = getStringAttr(message, 'state'); var stateString = base.getStringAttr(message, 'state');
console.log('hostStateChanged received: ', stateString); console.log('hostStateChanged received: ', stateString);
var state = remoting.HostSession.State.fromString(stateString); var state = remoting.HostSession.State.fromString(stateString);
switch (state) { switch (state) {
case remoting.HostSession.State.RECEIVED_ACCESS_CODE: case remoting.HostSession.State.RECEIVED_ACCESS_CODE:
var accessCode = getStringAttr(message, 'accessCode'); var accessCode = base.getStringAttr(message, 'accessCode');
var accessCodeLifetime = getNumberAttr(message, 'accessCodeLifetime'); var accessCodeLifetime =
base.getNumberAttr(message, 'accessCodeLifetime');
this.onReceivedAccessCode_(accessCode, accessCodeLifetime); this.onReceivedAccessCode_(accessCode, accessCodeLifetime);
break; break;
case remoting.HostSession.State.CONNECTED: case remoting.HostSession.State.CONNECTED:
var client = getStringAttr(message, 'client'); var client = base.getStringAttr(message, 'client');
this.onConnected_(client); this.onConnected_(client);
break; break;
} }
...@@ -260,13 +261,13 @@ remoting.It2MeHostFacade.prototype.onIncomingMessage_ = ...@@ -260,13 +261,13 @@ remoting.It2MeHostFacade.prototype.onIncomingMessage_ =
case 'natPolicyChanged': case 'natPolicyChanged':
if (this.onNatPolicyChanged_) { if (this.onNatPolicyChanged_) {
var natTraversalEnabled = var natTraversalEnabled =
getBooleanAttr(message, 'natTraversalEnabled'); base.getBooleanAttr(message, 'natTraversalEnabled');
this.onNatPolicyChanged_(natTraversalEnabled); this.onNatPolicyChanged_(natTraversalEnabled);
} }
break; break;
case 'error': case 'error':
console.error(getStringAttr(message, 'description')); console.error(base.getStringAttr(message, 'description'));
if (this.onError_) { if (this.onError_) {
this.onError_(remoting.Error.unexpected()); this.onError_(remoting.Error.unexpected());
} }
......
...@@ -2,6 +2,148 @@ ...@@ -2,6 +2,148 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
/** @suppress {duplicate} */
var remoting = remoting || {};
(function() {
'use strict';
/**
* @param {*} value
* @return {boolean}
*/
var isArray = function(value) {
return Array.isArray(value);
};
/**
* @param {*} value
* @return {boolean}
*/
var isBoolean = function(value) {
return typeof value == 'boolean';
};
/**
* @param {*} value
* @return {boolean}
*/
var isNumber = function(value) {
return typeof value == 'number';
};
/**
* @param {*} value
* @return {boolean}
*/
var isObject = function(value) {
return value != null && typeof value == 'object' && !Array.isArray(value);
};
/**
* @param {*} value
* @return {boolean}
*/
var isString = function(value) {
return typeof value == 'string';
};
/**
* @param {*} value
* @return {string}
*/
var jsonTypeOf = function(value) {
if (typeof value == 'object') {
if (value === null) {
return 'null';
} else if (Array.isArray(value)) {
return 'array';
} else {
return 'object';
}
} else {
return typeof value;
}
};
/**
* @param {*} value the value to check; must be an object
* @param {function(*):boolean} pred
* @param {string} typeDesc
* @return {*} the argument
*/
var assertType = function(value, pred, typeDesc) {
if (pred(value)) {
return value;
} else {
throw new Error('Invalid data type' +
' (expected: ' + typeDesc +
', actual: ' + jsonTypeOf(value) + ')');
}
};
/**
* @param {*} value the value to check; must be an object
* @return {!Array} the argument
*/
base.assertArray = function(value) {
return /** @type {!Array} */ (assertType(value, isArray, 'array'));
};
/**
* @param {*} value the value to check; must be a boolean
* @return {boolean} the argument
*/
base.assertBoolean = function(value) {
return /** @type {boolean} */ (assertType(value, isBoolean, 'boolean'));
};
/**
* @param {*} value the value to check; must be a number
* @return {number} the argument
*/
base.assertNumber = function(value) {
return /** @type {number} */ (assertType(value, isNumber, 'number'));
};
/**
* @param {*} value the value to check; must be an object
* @return {!Object} the argument
*/
base.assertObject = function(value) {
return /** @type {!Object} */ (assertType(value, isObject, 'object'));
};
/**
* @param {*} value the value to check; must be a string
* @return {string} the argument
*/
base.assertString = function(value) {
return /** @type {string} */ (assertType(value, isString, 'string'));
};
/**
* @param {Object<string,*>} dict The dictionary containing the |key|
* @param {string} key The key to typecheck in the |dict|.
* @param {function(*):boolean} pred
* @param {string} typeDesc
* @param {*=} opt_default The value to return if pred returns false.
* @return {*} The |key| attribute value.
*/
var getTypedAttr = function(dict, key, pred, typeDesc, opt_default) {
var value = /** @type {*} */ (dict[key]);
if (pred(value)) {
return value;
} else if (opt_default !== undefined) {
return opt_default;
} else {
throw new Error('Invalid data type for ' + key +
' (expected: ' + typeDesc + ', actual: ' +
jsonTypeOf(value) + ')');
}
};
/** /**
* Get the |key| attribute in the given |dict| and verify that it is an * Get the |key| attribute in the given |dict| and verify that it is an
* array value. * array value.
...@@ -14,18 +156,10 @@ ...@@ -14,18 +156,10 @@
* @param {Array=} opt_default The value to return if the key is not a bool. * @param {Array=} opt_default The value to return if the key is not a bool.
* @return {Array} The |key| attribute value as an object. * @return {Array} The |key| attribute value as an object.
*/ */
function getArrayAttr(dict, key, opt_default) { base.getArrayAttr = function(dict, key, opt_default) {
var value = /** @type {Array} */ (dict[key]); return /** @type {Array} */ (
if (!(value instanceof Array)) { getTypedAttr(dict, key, isArray, 'array', opt_default));
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 * Get the |key| attribute in the given |dict| and verify that it is a
...@@ -39,21 +173,14 @@ function getArrayAttr(dict, key, opt_default) { ...@@ -39,21 +173,14 @@ function getArrayAttr(dict, key, opt_default) {
* @param {boolean=} opt_default The value to return if the key is not a bool. * @param {boolean=} opt_default The value to return if the key is not a bool.
* @return {boolean} The |key| attribute value as a boolean. * @return {boolean} The |key| attribute value as a boolean.
*/ */
function getBooleanAttr(dict, key, opt_default) { base.getBooleanAttr = function(dict, key, opt_default) {
var value = /** @type {boolean} */ (dict[key]); var value = /** @type {*} */ (dict[key]);
if (value == 'true' || value == 'false') { if (value == 'true' || value == 'false') {
return (value == 'true'); return value == 'true';
}
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; return /** @type {boolean} */ (
} getTypedAttr(dict, key, isBoolean, 'boolean', opt_default));
};
/** /**
* Get the |key| attribute in the given |dict| and verify that it is a * Get the |key| attribute in the given |dict| and verify that it is a
...@@ -67,18 +194,10 @@ function getBooleanAttr(dict, key, opt_default) { ...@@ -67,18 +194,10 @@ function getBooleanAttr(dict, key, opt_default) {
* @param {number=} opt_default The value to return if the key is not a number. * @param {number=} opt_default The value to return if the key is not a number.
* @return {number} The |key| attribute value as a number. * @return {number} The |key| attribute value as a number.
*/ */
function getNumberAttr(dict, key, opt_default) { base.getNumberAttr = function(dict, key, opt_default) {
var value = /** @type {number} */(dict[key]); return /** @type {number} */ (
if (typeof value != 'number') { getTypedAttr(dict, key, isNumber, 'number', opt_default));
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 * Get the |key| attribute in the given |dict| and verify that it is an
...@@ -90,20 +209,12 @@ function getNumberAttr(dict, key, opt_default) { ...@@ -90,20 +209,12 @@ function getNumberAttr(dict, key, opt_default) {
* @param {Object<string,*>} dict The dictionary containing the |key| * @param {Object<string,*>} dict The dictionary containing the |key|
* @param {string} key The key to typecheck in the |dict|. * @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. * @param {Object=} opt_default The value to return if the key is not a bool.
* @return {Object} The |key| attribute value as an object. * @return {!Object} The |key| attribute value as an object.
*/ */
function getObjectAttr(dict, key, opt_default) { base.getObjectAttr = function(dict, key, opt_default) {
var value = /** @type {Object} */ (dict[key]); return /** @type {!Object} */ (
if (typeof value != 'object') { getTypedAttr(dict, key, isObject, 'object', opt_default));
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 * Get the |key| attribute in the given |dict| and verify that it is a
...@@ -117,18 +228,10 @@ function getObjectAttr(dict, key, opt_default) { ...@@ -117,18 +228,10 @@ function getObjectAttr(dict, key, opt_default) {
* @param {string=} opt_default The value to return if the key is not a string. * @param {string=} opt_default The value to return if the key is not a string.
* @return {string} The |key| attribute value as a string. * @return {string} The |key| attribute value as a string.
*/ */
function getStringAttr(dict, key, opt_default) { base.getStringAttr = function(dict, key, opt_default) {
var value = /** @type {string} */ (dict[key]); return /** @type {string} */ (
if (typeof value != 'string') { getTypedAttr(dict, key, isString, 'string', opt_default));
if (opt_default === undefined) { };
throw 'Invalid data type for ' + key +
' (expected: string, actual: ' + typeof value + ')';
} else {
return opt_default;
}
}
return value;
}
/** /**
* Return a JSON object parsed from a string. * Return a JSON object parsed from a string.
...@@ -139,10 +242,8 @@ function getStringAttr(dict, key, opt_default) { ...@@ -139,10 +242,8 @@ function getStringAttr(dict, key, opt_default) {
* @param {string} jsonString The JSON string to parse. * @param {string} jsonString The JSON string to parse.
* @return {Object} The JSON object created from the |jsonString|. * @return {Object} The JSON object created from the |jsonString|.
*/ */
function getJsonObjectFromString(jsonString) { base.getJsonObjectFromString = function(jsonString) {
var value = base.jsonParseSafe(jsonString); return base.assertObject(base.jsonParseSafe(jsonString));
if (typeof value != 'object') { };
throw 'Invalid data type (expected: Object, actual: ' + typeof value + ')';
} })();
return value; \ No newline at end of file
}
This diff is collapsed.
...@@ -63,8 +63,8 @@ remoting.VideoFrameRecorder.prototype.handleMessage = ...@@ -63,8 +63,8 @@ remoting.VideoFrameRecorder.prototype.handleMessage =
return false; return false;
} }
var messageType = getStringAttr(message, 'type'); var messageType = base.getStringAttr(message, 'type');
var messageData = getStringAttr(message, 'data'); var messageData = base.getStringAttr(message, 'data');
if (messageType == 'next-frame-reply') { if (messageType == 'next-frame-reply') {
if (!this.fileWriter_) { if (!this.fileWriter_) {
......
...@@ -68,6 +68,20 @@ QUnit.Assert.prototype.equal = function(a, b, opt_message) {}; ...@@ -68,6 +68,20 @@ QUnit.Assert.prototype.equal = function(a, b, opt_message) {};
*/ */
QUnit.Assert.prototype.expect = function(assertionCount) {}; QUnit.Assert.prototype.expect = function(assertionCount) {};
/**
* @param {*} a
* @param {*} b
* @param {string=} opt_message
*/
QUnit.Assert.prototype.strictEqual = function(a, b, opt_message) {};
/**
* @param {function()} a
* @param {*=} opt_b
* @param {string=} opt_message
*/
QUnit.Assert.prototype.throws = function(a, opt_b, opt_message) {};
/** /**
* @typedef {{ * @typedef {{
* beforeEach: (function(QUnit.Assert=) | undefined), * beforeEach: (function(QUnit.Assert=) | undefined),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment