diff options
author | jrw <jrw@chromium.org> | 2015-03-19 13:08:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-19 20:09:52 +0000 |
commit | b814ba0d3921aaf296ebe1b653ab2827f671a7dd (patch) | |
tree | dc9c27a220e23b9aa18603de67c33eeb50212b2e /remoting | |
parent | 43dc9613e9badb5e0c6a11d25106271d2f23899f (diff) | |
download | chromium_src-b814ba0d3921aaf296ebe1b653ab2827f671a7dd.zip chromium_src-b814ba0d3921aaf296ebe1b653ab2827f671a7dd.tar.gz chromium_src-b814ba0d3921aaf296ebe1b653ab2827f671a7dd.tar.bz2 |
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}
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/remoting_webapp_files.gypi | 1 | ||||
-rw-r--r-- | remoting/webapp/app_remoting/js/app_remoting.js | 14 | ||||
-rw-r--r-- | remoting/webapp/base/js/base.js | 1 | ||||
-rw-r--r-- | remoting/webapp/crd/js/cast_extension_handler.js | 2 | ||||
-rw-r--r-- | remoting/webapp/crd/js/client_plugin_host_desktop_impl.js | 10 | ||||
-rw-r--r-- | remoting/webapp/crd/js/client_plugin_impl.js | 70 | ||||
-rw-r--r-- | remoting/webapp/crd/js/gnubby_auth_handler.js | 8 | ||||
-rw-r--r-- | remoting/webapp/crd/js/host.js | 10 | ||||
-rw-r--r-- | remoting/webapp/crd/js/host_daemon_facade.js | 39 | ||||
-rw-r--r-- | remoting/webapp/crd/js/host_list_api_impl.js | 20 | ||||
-rw-r--r-- | remoting/webapp/crd/js/it2me_host_facade.js | 17 | ||||
-rw-r--r-- | remoting/webapp/crd/js/typecheck.js | 241 | ||||
-rw-r--r-- | remoting/webapp/crd/js/typecheck_unittest.js | 240 | ||||
-rw-r--r-- | remoting/webapp/crd/js/video_frame_recorder.js | 4 | ||||
-rw-r--r-- | remoting/webapp/js_proto/qunit_proto.js | 14 |
15 files changed, 526 insertions, 165 deletions
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi index 84586be..165c0d8 100644 --- a/remoting/remoting_webapp_files.gypi +++ b/remoting/remoting_webapp_files.gypi @@ -77,6 +77,7 @@ 'webapp/crd/js/identity_unittest.js', 'webapp/crd/js/l10n_unittest.js', 'webapp/crd/js/menu_button_unittest.js', + 'webapp/crd/js/typecheck_unittest.js', 'webapp/crd/js/xhr_unittest.js', 'webapp/crd/js/xmpp_connection_unittest.js', 'webapp/crd/js/xmpp_login_handler_unittest.js', diff --git a/remoting/webapp/app_remoting/js/app_remoting.js b/remoting/webapp/app_remoting/js/app_remoting.js index 83493eb..f6b46a6 100644 --- a/remoting/webapp/app_remoting/js/app_remoting.js +++ b/remoting/webapp/app_remoting/js/app_remoting.js @@ -315,7 +315,7 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { case 'openURL': // URL requests from the hosted app are untrusted, so disallow anything // 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) { console.error('Bad URL: ' + url); } else { @@ -324,13 +324,13 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { return true; case 'onWindowRemoved': - var id = getNumberAttr(message, 'id'); + var id = base.getNumberAttr(message, 'id'); this.windowActivationMenu_.remove(id); return true; case 'onWindowAdded': - var id = getNumberAttr(message, 'id'); - var title = getStringAttr(message, 'title'); + var id = base.getNumberAttr(message, 'id'); + var title = base.getStringAttr(message, 'title'); this.windowActivationMenu_.add(id, title); return true; @@ -339,15 +339,15 @@ remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { return true; case 'setKeyboardLayouts': - var supportedLayouts = getArrayAttr(message, 'supportedLayouts'); - var currentLayout = getStringAttr(message, 'currentLayout'); + var supportedLayouts = base.getArrayAttr(message, 'supportedLayouts'); + var currentLayout = base.getStringAttr(message, 'currentLayout'); console.log('Current host keyboard layout: ' + currentLayout); console.log('Supported host keyboard layouts: ' + supportedLayouts); this.keyboardLayoutsMenu_.setLayouts(supportedLayouts, currentLayout); return true; case 'pingResponse': - var then = getNumberAttr(message, 'timestamp'); + var then = base.getNumberAttr(message, 'timestamp'); var now = new Date().getTime(); this.contextMenu_.updateConnectionRTT(now - then); return true; diff --git a/remoting/webapp/base/js/base.js b/remoting/webapp/base/js/base.js index 51fff37..e963f32 100644 --- a/remoting/webapp/base/js/base.js +++ b/remoting/webapp/base/js/base.js @@ -34,7 +34,6 @@ base.debug.assert = function(expr, opt_msg) { if (opt_msg) { msg += ' ' + opt_msg; } - console.error(msg); if (base.debug.breakOnAssert) { alert(msg); debugger; diff --git a/remoting/webapp/crd/js/cast_extension_handler.js b/remoting/webapp/crd/js/cast_extension_handler.js index b8e2621..72cac12 100644 --- a/remoting/webapp/crd/js/cast_extension_handler.js +++ b/remoting/webapp/crd/js/cast_extension_handler.js @@ -251,7 +251,7 @@ remoting.CastExtensionHandler.prototype.chromotingMessageListener = function(ns, message) { if (ns === this.kCastNamespace_) { try { - var messageObj = getJsonObjectFromString(message); + var messageObj = base.getJsonObjectFromString(message); this.sendMessageToHost_(messageObj); } catch (err) { console.error('Failed to process message from Cast device.'); diff --git a/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js b/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js index 78ca6df..71cb164 100644 --- a/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js +++ b/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js @@ -86,10 +86,10 @@ remoting.ClientPlugin.HostDesktopImpl.prototype.resize = function( */ remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function( message) { - this.width_ = getNumberAttr(message.data, 'width'); - this.height_ = getNumberAttr(message.data, 'height'); - this.xDpi_ = getNumberAttr(message.data, 'x_dpi', 96); - this.yDpi_ = getNumberAttr(message.data, 'y_dpi', 96); + this.width_ = base.getNumberAttr(message.data, 'width'); + this.height_ = base.getNumberAttr(message.data, 'height'); + this.xDpi_ = base.getNumberAttr(message.data, 'x_dpi', 96); + this.yDpi_ = base.getNumberAttr(message.data, 'y_dpi', 96); this.raiseEvent(remoting.HostDesktop.Events.sizeChanged, this.getDimensions()); }; @@ -104,7 +104,7 @@ remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function( */ remoting.ClientPlugin.HostDesktopImpl.prototype.onShapeUpdated = function(message) { - var shapes = getArrayAttr(message.data, 'rects'); + var shapes = base.getArrayAttr(message.data, 'rects'); var rects = shapes.map( /** @param {Array.<number>} shape */ function(shape) { diff --git a/remoting/webapp/crd/js/client_plugin_impl.js b/remoting/webapp/crd/js/client_plugin_impl.js index 2b94066..a0de1e3 100644 --- a/remoting/webapp/crd/js/client_plugin_impl.js +++ b/remoting/webapp/crd/js/client_plugin_impl.js @@ -212,35 +212,36 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { var handler = this.connectionEventHandler_; if (message.method == 'sendOutgoingIq') { - handler.onOutgoingIq(getStringAttr(message.data, 'iq')); + handler.onOutgoingIq(base.getStringAttr(message.data, 'iq')); } else if (message.method == 'logDebugMessage') { - handler.onDebugMessage(getStringAttr(message.data, 'message')); + handler.onDebugMessage(base.getStringAttr(message.data, 'message')); } else if (message.method == 'onConnectionStatus') { var state = remoting.ClientSession.State.fromString( - getStringAttr(message.data, 'state')); + base.getStringAttr(message.data, 'state')); var error = remoting.ClientSession.ConnectionError.fromString( - getStringAttr(message.data, 'error')); + base.getStringAttr(message.data, 'error')); handler.onConnectionStatusUpdate(state, error); } else if (message.method == 'onRouteChanged') { - var channel = getStringAttr(message.data, 'channel'); - var connectionType = getStringAttr(message.data, 'connectionType'); + var channel = base.getStringAttr(message.data, 'channel'); + var connectionType = base.getStringAttr(message.data, 'connectionType'); handler.onRouteChanged(channel, connectionType); } else if (message.method == 'onConnectionReady') { - var ready = getBooleanAttr(message.data, 'ready'); + var ready = base.getBooleanAttr(message.data, 'ready'); handler.onConnectionReady(ready); } else if (message.method == 'setCapabilities') { /** @type {!Array<string>} */ - var capabilities = tokenize(getStringAttr(message.data, 'capabilities')); + var capabilities = tokenize( + base.getStringAttr(message.data, 'capabilities')); handler.onSetCapabilities(capabilities); } else if (message.method == 'extensionMessage') { - var extMsgType = getStringAttr(message.data, 'type'); - var extMsgData = getStringAttr(message.data, 'data'); + var extMsgType = base.getStringAttr(message.data, 'type'); + var extMsgData = base.getStringAttr(message.data, 'data'); handler.onExtensionMessage(extMsgType, extMsgData); } } @@ -248,19 +249,20 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { if (message.method == 'hello') { // Resize in case we had to enlarge it to support click-to-play. this.hidePluginForClickToPlay_(); - this.pluginApiVersion_ = getNumberAttr(message.data, 'apiVersion'); - this.pluginApiMinVersion_ = getNumberAttr(message.data, 'apiMinVersion'); + this.pluginApiVersion_ = base.getNumberAttr(message.data, 'apiVersion'); + this.pluginApiMinVersion_ = + base.getNumberAttr(message.data, 'apiMinVersion'); if (this.pluginApiVersion_ >= 7) { this.pluginApiFeatures_ = - tokenize(getStringAttr(message.data, 'apiFeatures')); + tokenize(base.getStringAttr(message.data, 'apiFeatures')); // Negotiate capabilities. /** @type {!Array<string>} */ var supportedCapabilities = []; if ('supportedCapabilities' in message.data) { supportedCapabilities = - tokenize(getStringAttr(message.data, 'supportedCapabilities')); + tokenize(base.getStringAttr(message.data, 'supportedCapabilities')); } // At the moment the webapp does not recognize any of // 'requestedCapabilities' capabilities (so they all should be disabled) @@ -287,19 +289,19 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { } else if (message.method == 'onPerfStats') { // 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'); + base.getNumberAttr(message.data, 'videoBandwidth'); + base.getNumberAttr(message.data, 'videoFrameRate'); + base.getNumberAttr(message.data, 'captureLatency'); + base.getNumberAttr(message.data, 'encodeLatency'); + base.getNumberAttr(message.data, 'decodeLatency'); + base.getNumberAttr(message.data, 'renderLatency'); + base.getNumberAttr(message.data, 'roundtripLatency'); this.perfStats_ = /** @type {remoting.ClientSession.PerfStats} */ (message.data); } else if (message.method == 'injectClipboardItem') { - var mimetype = getStringAttr(message.data, 'mimeType'); - var item = getStringAttr(message.data, 'item'); + var mimetype = base.getStringAttr(message.data, 'mimeType'); + var item = base.getStringAttr(message.data, 'item'); if (remoting.clipboard) { remoting.clipboard.fromHost(mimetype, item); } @@ -313,33 +315,33 @@ remoting.ClientPluginImpl.prototype.handleMessageMethod_ = function(message) { // 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. - var pairingSupported = getBooleanAttr(message.data, 'pairingSupported', + var pairingSupported = base.getBooleanAttr(message.data, 'pairingSupported', false); this.credentials_.getPIN(pairingSupported).then( this.onPinFetched_.bind(this) ); } else if (message.method == 'fetchThirdPartyToken') { - var tokenUrl = getStringAttr(message.data, 'tokenUrl'); - var hostPublicKey = getStringAttr(message.data, 'hostPublicKey'); - var scope = getStringAttr(message.data, 'scope'); + var tokenUrl = base.getStringAttr(message.data, 'tokenUrl'); + var hostPublicKey = base.getStringAttr(message.data, 'hostPublicKey'); + var scope = base.getStringAttr(message.data, 'scope'); this.credentials_.getThirdPartyToken(tokenUrl, hostPublicKey, scope).then( this.onThirdPartyTokenFetched_.bind(this) ); } else if (message.method == 'pairingResponse') { - var clientId = getStringAttr(message.data, 'clientId'); - var sharedSecret = getStringAttr(message.data, 'sharedSecret'); + var clientId = base.getStringAttr(message.data, 'clientId'); + var sharedSecret = base.getStringAttr(message.data, 'sharedSecret'); this.onPairingComplete_(clientId, sharedSecret); } else if (message.method == 'unsetCursorShape') { this.updateMouseCursorImage_('', 0, 0); } else if (message.method == 'setCursorShape') { - var width = getNumberAttr(message.data, 'width'); - var height = getNumberAttr(message.data, 'height'); - var hotspotX = getNumberAttr(message.data, 'hotspotX'); - var hotspotY = getNumberAttr(message.data, 'hotspotY'); - var srcArrayBuffer = getObjectAttr(message.data, 'data'); + var width = base.getNumberAttr(message.data, 'width'); + var height = base.getNumberAttr(message.data, 'height'); + var hotspotX = base.getNumberAttr(message.data, 'hotspotX'); + var hotspotY = base.getNumberAttr(message.data, 'hotspotY'); + var srcArrayBuffer = base.getObjectAttr(message.data, 'data'); var canvas = /** @type {HTMLCanvasElement} */ (document.createElement('canvas')); diff --git a/remoting/webapp/crd/js/gnubby_auth_handler.js b/remoting/webapp/crd/js/gnubby_auth_handler.js index f3514dd..724bbdc 100644 --- a/remoting/webapp/crd/js/gnubby_auth_handler.js +++ b/remoting/webapp/crd/js/gnubby_auth_handler.js @@ -62,12 +62,12 @@ remoting.GnubbyAuthHandler.prototype.sendMessageToHost_ = function(data) { */ remoting.GnubbyAuthHandler.prototype.onExtensionMessage = function(type, message) { - var messageType = getStringAttr(message, 'type'); + var messageType = base.getStringAttr(message, 'type'); if (messageType == 'data') { this.sendMessageToGnubbyd_({ 'type': 'auth-agent@openssh.com', - 'data': getArrayAttr(message, 'data') - }, this.callback_.bind(this, getNumberAttr(message, 'connectionId'))); + 'data': base.getArrayAttr(message, 'data') + }, this.callback_.bind(this, base.getNumberAttr(message, 'connectionId'))); } else { console.error('Invalid gnubby-auth message: ' + messageType); return false; @@ -87,7 +87,7 @@ remoting.GnubbyAuthHandler.prototype.callback_ = this.sendMessageToHost_({ 'type': 'data', 'connectionId': connectionId, - 'data': getArrayAttr(response, 'data') + 'data': base.getArrayAttr(response, 'data') }); } catch (/** @type {*} */ err) { console.error('gnubby callback failed: ', err); diff --git a/remoting/webapp/crd/js/host.js b/remoting/webapp/crd/js/host.js index 6bc0bba..9070b43 100644 --- a/remoting/webapp/crd/js/host.js +++ b/remoting/webapp/crd/js/host.js @@ -86,13 +86,13 @@ remoting.Host.Options.prototype.load = function() { // TODO(kelvinp): Uses a separate host options for app-remoting that // hardcodes resizeToClient to true. that.resizeToClient = - getBooleanAttr(options, 'resizeToClient', true); - that.shrinkToFit = getBooleanAttr(options, 'shrinkToFit', true); - that.desktopScale = getNumberAttr(options, 'desktopScale', 1); - that.remapKeys = getStringAttr(options, 'remapKeys', ''); + base.getBooleanAttr(options, 'resizeToClient', true); + that.shrinkToFit = base.getBooleanAttr(options, 'shrinkToFit', true); + that.desktopScale = base.getNumberAttr(options, 'desktopScale', 1); + that.remapKeys = base.getStringAttr(options, 'remapKeys', ''); that.pairingInfo = /** @type {remoting.PairingInfo} */ ( - getObjectAttr(options, 'pairingInfo', that.pairingInfo)); + base.getObjectAttr(options, 'pairingInfo', that.pairingInfo)); }); }; diff --git a/remoting/webapp/crd/js/host_daemon_facade.js b/remoting/webapp/crd/js/host_daemon_facade.js index eab06de..c09b374 100644 --- a/remoting/webapp/crd/js/host_daemon_facade.js +++ b/remoting/webapp/crd/js/host_daemon_facade.js @@ -199,7 +199,7 @@ remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) { delete this.pendingReplies_[id]; try { - var type = getStringAttr(message, 'type'); + var type = base.getStringAttr(message, 'type'); if (type != reply.type) { throw 'Expected reply type: ' + reply.type + ', got: ' + type; } @@ -222,58 +222,59 @@ remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) { */ remoting.HostDaemonFacade.prototype.handleIncomingMessage_ = function(message, onDone) { - var type = getStringAttr(message, 'type'); + var type = base.getStringAttr(message, 'type'); switch (type) { 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. // Those versions default to the empty list of supported features. - this.supportedFeatures_ = getArrayAttr(message, 'supportedFeatures', []); + this.supportedFeatures_ = + base.getArrayAttr(message, 'supportedFeatures', []); onDone(); break; case 'getHostNameResponse': - onDone(getStringAttr(message, 'hostname')); + onDone(base.getStringAttr(message, 'hostname')); break; case 'getPinHashResponse': - onDone(getStringAttr(message, 'hash')); + onDone(base.getStringAttr(message, 'hash')); break; case 'generateKeyPairResponse': - var privateKey = getStringAttr(message, 'privateKey'); - var publicKey = getStringAttr(message, 'publicKey'); + var privateKey = base.getStringAttr(message, 'privateKey'); + var publicKey = base.getStringAttr(message, 'publicKey'); onDone(privateKey, publicKey); break; case 'updateDaemonConfigResponse': var result = remoting.HostController.AsyncResult.fromString( - getStringAttr(message, 'result')); + base.getStringAttr(message, 'result')); onDone(result); break; case 'getDaemonConfigResponse': - onDone(getObjectAttr(message, 'config')); + onDone(base.getObjectAttr(message, 'config')); break; case 'getUsageStatsConsentResponse': - var supported = getBooleanAttr(message, 'supported'); - var allowed = getBooleanAttr(message, 'allowed'); - var setByPolicy = getBooleanAttr(message, 'setByPolicy'); + var supported = base.getBooleanAttr(message, 'supported'); + var allowed = base.getBooleanAttr(message, 'allowed'); + var setByPolicy = base.getBooleanAttr(message, 'setByPolicy'); onDone(supported, allowed, setByPolicy); break; case 'startDaemonResponse': case 'stopDaemonResponse': var result = remoting.HostController.AsyncResult.fromString( - getStringAttr(message, 'result')); + base.getStringAttr(message, 'result')); onDone(result); break; case 'getDaemonStateResponse': var state = remoting.HostController.State.fromString( - getStringAttr(message, 'state')); + base.getStringAttr(message, 'state')); onDone(state); break; @@ -289,16 +290,16 @@ remoting.HostDaemonFacade.prototype.handleIncomingMessage_ = case 'clearPairedClientsResponse': case 'deletePairedClientResponse': - onDone(getBooleanAttr(message, 'result')); + onDone(base.getBooleanAttr(message, 'result')); break; case 'getHostClientIdResponse': - onDone(getStringAttr(message, 'clientId')); + onDone(base.getStringAttr(message, 'clientId')); break; case 'getCredentialsFromAuthCodeResponse': - var userEmail = getStringAttr(message, 'userEmail'); - var refreshToken = getStringAttr(message, 'refreshToken'); + var userEmail = base.getStringAttr(message, 'userEmail'); + var refreshToken = base.getStringAttr(message, 'refreshToken'); if (userEmail && refreshToken) { onDone(userEmail, refreshToken); } else { diff --git a/remoting/webapp/crd/js/host_list_api_impl.js b/remoting/webapp/crd/js/host_list_api_impl.js index bf5e25c..a4dbe08 100644 --- a/remoting/webapp/crd/js/host_list_api_impl.js +++ b/remoting/webapp/crd/js/host_list_api_impl.js @@ -118,15 +118,17 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ = var hosts = items.map( function(/** Object */ item) { var host = new remoting.Host(); - host.hostName = getStringAttr(item, 'hostName', ''); - host.hostId = getStringAttr(item, 'hostId', ''); - host.status = getStringAttr(item, 'status', ''); - host.jabberId = getStringAttr(item, 'jabberId', ''); - host.publicKey = getStringAttr(item, 'publicKey', ''); - host.hostVersion = getStringAttr(item, 'hostVersion', ''); - host.tokenUrlPatterns = getArrayAttr(item, 'tokenUrlPatterns', []); - host.updatedTime = getStringAttr(item, 'updatedTime', ''); - host.hostOfflineReason = getStringAttr(item, 'hostOfflineReason', ''); + host.hostName = base.getStringAttr(item, 'hostName', ''); + host.hostId = base.getStringAttr(item, 'hostId', ''); + host.status = base.getStringAttr(item, 'status', ''); + host.jabberId = base.getStringAttr(item, 'jabberId', ''); + host.publicKey = base.getStringAttr(item, 'publicKey', ''); + host.hostVersion = base.getStringAttr(item, 'hostVersion', ''); + host.tokenUrlPatterns = + base.getArrayAttr(item, 'tokenUrlPatterns', []); + host.updatedTime = base.getStringAttr(item, 'updatedTime', ''); + host.hostOfflineReason = + base.getStringAttr(item, 'hostOfflineReason', ''); return host; }); onDone(hosts); diff --git a/remoting/webapp/crd/js/it2me_host_facade.js b/remoting/webapp/crd/js/it2me_host_facade.js index 2106ec8..99800f3 100644 --- a/remoting/webapp/crd/js/it2me_host_facade.js +++ b/remoting/webapp/crd/js/it2me_host_facade.js @@ -208,11 +208,11 @@ remoting.It2MeHostFacade.prototype.getClient = function() { */ remoting.It2MeHostFacade.prototype.onIncomingMessage_ = function(message) { - var type = getStringAttr(message, 'type'); + var type = base.getStringAttr(message, 'type'); switch (type) { case 'helloResponse': - var version = getStringAttr(message, 'version'); + var version = base.getStringAttr(message, 'version'); console.log('Host version: ', version); this.initialized_ = true; // A "hello" request is sent immediately after the native messaging host @@ -236,19 +236,20 @@ remoting.It2MeHostFacade.prototype.onIncomingMessage_ = break; case 'hostStateChanged': - var stateString = getStringAttr(message, 'state'); + var stateString = base.getStringAttr(message, 'state'); console.log('hostStateChanged received: ', stateString); var state = remoting.HostSession.State.fromString(stateString); switch (state) { case remoting.HostSession.State.RECEIVED_ACCESS_CODE: - var accessCode = getStringAttr(message, 'accessCode'); - var accessCodeLifetime = getNumberAttr(message, 'accessCodeLifetime'); + var accessCode = base.getStringAttr(message, 'accessCode'); + var accessCodeLifetime = + base.getNumberAttr(message, 'accessCodeLifetime'); this.onReceivedAccessCode_(accessCode, accessCodeLifetime); break; case remoting.HostSession.State.CONNECTED: - var client = getStringAttr(message, 'client'); + var client = base.getStringAttr(message, 'client'); this.onConnected_(client); break; } @@ -260,13 +261,13 @@ remoting.It2MeHostFacade.prototype.onIncomingMessage_ = case 'natPolicyChanged': if (this.onNatPolicyChanged_) { var natTraversalEnabled = - getBooleanAttr(message, 'natTraversalEnabled'); + base.getBooleanAttr(message, 'natTraversalEnabled'); this.onNatPolicyChanged_(natTraversalEnabled); } break; case 'error': - console.error(getStringAttr(message, 'description')); + console.error(base.getStringAttr(message, 'description')); if (this.onError_) { this.onError_(remoting.Error.unexpected()); } diff --git a/remoting/webapp/crd/js/typecheck.js b/remoting/webapp/crd/js/typecheck.js index b44df12..10e1959 100644 --- a/remoting/webapp/crd/js/typecheck.js +++ b/remoting/webapp/crd/js/typecheck.js @@ -2,6 +2,148 @@ // Use of this source code is governed by a BSD-style license that can be // 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 * array value. @@ -14,18 +156,10 @@ * @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; -} +base.getArrayAttr = function(dict, key, opt_default) { + return /** @type {Array} */ ( + getTypedAttr(dict, key, isArray, 'array', opt_default)); +}; /** * Get the |key| attribute in the given |dict| and verify that it is a @@ -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. * @return {boolean} The |key| attribute value as a boolean. */ -function getBooleanAttr(dict, key, opt_default) { - var value = /** @type {boolean} */ (dict[key]); +base.getBooleanAttr = function(dict, key, opt_default) { + var value = /** @type {*} */ (dict[key]); if (value == 'true' || value == 'false') { - 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 == 'true'; } - 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 @@ -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. * @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; -} +base.getNumberAttr = function(dict, key, opt_default) { + return /** @type {number} */ ( + getTypedAttr(dict, key, isNumber, 'number', opt_default)); +}; /** * Get the |key| attribute in the given |dict| and verify that it is an @@ -90,20 +209,12 @@ function getNumberAttr(dict, key, 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; -} + * @return {!Object} The |key| attribute value as an object. + */ +base.getObjectAttr = function(dict, key, opt_default) { + return /** @type {!Object} */ ( + getTypedAttr(dict, key, isObject, 'object', opt_default)); +}; /** * Get the |key| attribute in the given |dict| and verify that it is a @@ -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. * @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; -} +base.getStringAttr = function(dict, key, opt_default) { + return /** @type {string} */ ( + getTypedAttr(dict, key, isString, 'string', opt_default)); +}; /** * Return a JSON object parsed from a string. @@ -139,10 +242,8 @@ function getStringAttr(dict, key, opt_default) { * @param {string} jsonString The JSON string to parse. * @return {Object} The JSON object created from the |jsonString|. */ -function getJsonObjectFromString(jsonString) { - var value = base.jsonParseSafe(jsonString); - if (typeof value != 'object') { - throw 'Invalid data type (expected: Object, actual: ' + typeof value + ')'; - } - return value; -} +base.getJsonObjectFromString = function(jsonString) { + return base.assertObject(base.jsonParseSafe(jsonString)); +}; + +})();
\ No newline at end of file diff --git a/remoting/webapp/crd/js/typecheck_unittest.js b/remoting/webapp/crd/js/typecheck_unittest.js new file mode 100644 index 0000000..c7b93c90 --- /dev/null +++ b/remoting/webapp/crd/js/typecheck_unittest.js @@ -0,0 +1,240 @@ +// Copyright 2015 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. + +(function() { + +'use strict'; + +var anArray = ['an array']; +var aBoolean = false; +var aNumber = 42; +var anObject = {name: 'my object'}; +var aString = 'woo'; + +var anotherArray = ['another array']; +var anotherBoolean = true; +var anotherNumber = -42; +var anotherObject = {name: 'a bad object'}; +var anotherString = 'boo!'; + +var testObj = { + anArray: anArray, + aBoolean: aBoolean, + aNumber: aNumber, + anObject: anObject, + aString: aString, + aNull: null, + anUndefined: undefined +}; + +QUnit.module('typecheck'); + +QUnit.test('base.assertArray', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.assertArray(anArray), anArray); + assert.throws(base.assertArray.bind(null, aBoolean), Error); + assert.throws(base.assertArray.bind(null, aNumber), Error); + assert.throws(base.assertArray.bind(null, anObject), Error); + assert.throws(base.assertArray.bind(null, aString), Error); + assert.throws(base.assertArray.bind(null, null), Error); + assert.throws(base.assertArray.bind(null, undefined), Error); +}); + +QUnit.test('base.assertBoolean', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.assertBoolean(aBoolean), aBoolean); + assert.throws(base.assertNumber.bind(null, anArray), Error); + assert.throws(base.assertBoolean.bind(null, aNumber), Error); + assert.throws(base.assertBoolean.bind(null, anObject), Error); + assert.throws(base.assertBoolean.bind(null, aString), Error); + assert.throws(base.assertBoolean.bind(null, null), Error); + assert.throws(base.assertBoolean.bind(null, undefined), Error); +}); + +QUnit.test('base.assertNumber', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.assertNumber(aNumber), aNumber); + assert.throws(base.assertNumber.bind(null, anArray), Error); + assert.throws(base.assertNumber.bind(null, aBoolean), Error); + assert.throws(base.assertNumber.bind(null, anObject), Error); + assert.throws(base.assertNumber.bind(null, aString), Error); + assert.throws(base.assertNumber.bind(null, null), Error); + assert.throws(base.assertNumber.bind(null, undefined), Error); +}); + +QUnit.test('base.assertObject', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.assertObject(anObject), anObject); + assert.throws(base.assertObject.bind(null, anArray), Error); + assert.throws(base.assertObject.bind(null, aBoolean), Error); + assert.throws(base.assertObject.bind(null, aNumber), Error); + assert.throws(base.assertObject.bind(null, aString), Error); + assert.throws(base.assertObject.bind(null, null), Error); + assert.throws(base.assertObject.bind(null, undefined), Error); +}); + +QUnit.test('base.assertString', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.assertString(aString), aString); + assert.throws(base.assertString.bind(null, anArray), Error); + assert.throws(base.assertString.bind(null, aBoolean), Error); + assert.throws(base.assertString.bind(null, aNumber), Error); + assert.throws(base.assertString.bind(null, anObject), Error); + assert.throws(base.assertString.bind(null, null), Error); + assert.throws(base.assertString.bind(null, undefined), Error); +}); + + +QUnit.test('base.getArrayAttr', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.getArrayAttr(testObj, 'anArray'), anArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'anArray', anotherArray), + anArray); + + assert.throws(base.getArrayAttr.bind(null, testObj, 'aBoolean'), Error); + assert.throws(base.getArrayAttr.bind(null, testObj, 'aNumber'), Error); + assert.throws(base.getArrayAttr.bind(null, testObj, 'anObject'), Error); + assert.throws(base.getArrayAttr.bind(null, testObj, 'aString'), Error); + assert.throws(base.getArrayAttr.bind(null, testObj, 'aNull'), Error); + assert.throws(base.getArrayAttr.bind(null, testObj, 'anUndefined'), Error); + assert.throws(base.getArrayAttr.bind(null, testObj, 'noSuchKey'), Error); + + assert.strictEqual( + base.getArrayAttr(testObj, 'aBoolean', anotherArray), anotherArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'aNumber', anotherArray), anotherArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'anObject', anotherArray), anotherArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'aString', anotherArray), anotherArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'aNull', anotherArray), anotherArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'anUndefined', anotherArray), anotherArray); + assert.strictEqual( + base.getArrayAttr(testObj, 'noSuchKey', anotherArray), anotherArray); +}); + +QUnit.test('base.getBooleanAttr', function(/** QUnit.Assert */ assert) { + assert.strictEqual(base.getBooleanAttr(testObj, 'aBoolean'), aBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'aBoolean', anotherBoolean), aBoolean); + + assert.throws(base.getBooleanAttr.bind(null, testObj, 'anArray'), Error); + assert.throws(base.getBooleanAttr.bind(null, testObj, 'aNumber'), Error); + assert.throws(base.getBooleanAttr.bind(null, testObj, 'anObject'), Error); + assert.throws(base.getBooleanAttr.bind(null, testObj, 'aString'), Error); + assert.throws(base.getBooleanAttr.bind(null, testObj, 'aNull'), Error); + assert.throws(base.getBooleanAttr.bind(null, testObj, 'anUndefined'), Error); + assert.throws(base.getBooleanAttr.bind(null, testObj, 'noSuchKey'), Error); + + assert.strictEqual( + base.getBooleanAttr(testObj, 'anArray', anotherBoolean), + anotherBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'aNumber', anotherBoolean), + anotherBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'anObject', anotherBoolean), + anotherBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'aString', anotherBoolean), + anotherBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'aNull', anotherBoolean), + anotherBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'anUndefined', anotherBoolean), + anotherBoolean); + assert.strictEqual( + base.getBooleanAttr(testObj, 'noSuchKey', anotherBoolean), + anotherBoolean); +}); + +QUnit.test('base.getNumberAttr', function(/** QUnit.Assert */ assert) { + assert.strictEqual( + base.getNumberAttr(testObj, 'aNumber'), aNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'aNumber', anotherNumber), aNumber); + + assert.throws(base.getNumberAttr.bind(null, testObj, 'anArray'), Error); + assert.throws(base.getNumberAttr.bind(null, testObj, 'aBoolean'), Error); + assert.throws(base.getNumberAttr.bind(null, testObj, 'anObject'), Error); + assert.throws(base.getNumberAttr.bind(null, testObj, 'aString'), Error); + assert.throws(base.getNumberAttr.bind(null, testObj, 'aNull'), Error); + assert.throws(base.getNumberAttr.bind(null, testObj, 'anUndefined'), Error); + assert.throws(base.getNumberAttr.bind(null, testObj, 'noSuchKey'), Error); + + assert.strictEqual( + base.getNumberAttr(testObj, 'anArray', anotherNumber), anotherNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'aBoolean', anotherNumber), anotherNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'anObject', anotherNumber), anotherNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'aString', anotherNumber), anotherNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'aNull', anotherNumber), anotherNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'anUndefined', anotherNumber), anotherNumber); + assert.strictEqual( + base.getNumberAttr(testObj, 'noSuchKey', anotherNumber), anotherNumber); +}); + +QUnit.test('base.getObjectAttr', function(/** QUnit.Assert */ assert) { + assert.strictEqual( + base.getObjectAttr(testObj, 'anObject'), anObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'anObject', anotherObject), anObject); + + assert.throws(base.getObjectAttr.bind(null, testObj, 'anArray'), Error); + assert.throws(base.getObjectAttr.bind(null, testObj, 'aBoolean'), Error); + assert.throws(base.getObjectAttr.bind(null, testObj, 'aNumber'), Error); + assert.throws(base.getObjectAttr.bind(null, testObj, 'aString'), Error); + assert.throws(base.getObjectAttr.bind(null, testObj, 'aNull'), Error); + assert.throws(base.getObjectAttr.bind(null, testObj, 'anUndefined'), Error); + assert.throws(base.getObjectAttr.bind(null, testObj, 'noSuchKey'), Error); + + assert.strictEqual( + base.getObjectAttr(testObj, 'anArray', anotherObject), anotherObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'aBoolean', anotherObject), anotherObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'aNumber', anotherObject), anotherObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'aString', anotherObject), anotherObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'aNull', anotherObject), anotherObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'anUndefined', anotherObject), anotherObject); + assert.strictEqual( + base.getObjectAttr(testObj, 'noSuchKey', anotherObject), anotherObject); +}); + +QUnit.test('base.getStringAttr', function(/** QUnit.Assert */ assert) { + assert.strictEqual( + base.getStringAttr(testObj, 'aString'), aString); + assert.strictEqual( + base.getStringAttr(testObj, 'aString', anotherString), aString); + + assert.throws(base.getStringAttr.bind(null, testObj, 'anArray'), Error); + assert.throws(base.getStringAttr.bind(null, testObj, 'aBoolean'), Error); + assert.throws(base.getStringAttr.bind(null, testObj, 'aNumber'), Error); + assert.throws(base.getStringAttr.bind(null, testObj, 'anObject'), Error); + assert.throws(base.getStringAttr.bind(null, testObj, 'aNull'), Error); + assert.throws(base.getStringAttr.bind(null, testObj, 'anUndefined'), Error); + assert.throws(base.getStringAttr.bind(null, testObj, 'noSuchKey'), Error); + + assert.strictEqual( + base.getStringAttr(testObj, 'anArray', anotherString), anotherString); + assert.strictEqual( + base.getStringAttr(testObj, 'aBoolean', anotherString), anotherString); + assert.strictEqual( + base.getStringAttr(testObj, 'aNumber', anotherString), anotherString); + assert.strictEqual( + base.getStringAttr(testObj, 'anObject', anotherString), anotherString); + assert.strictEqual( + base.getStringAttr(testObj, 'aNull', anotherString), anotherString); + assert.strictEqual( + base.getStringAttr(testObj, 'anUndefined', anotherString), anotherString); + assert.strictEqual( + base.getStringAttr(testObj, 'noSuchKey', anotherString), anotherString); +}); + +})(); diff --git a/remoting/webapp/crd/js/video_frame_recorder.js b/remoting/webapp/crd/js/video_frame_recorder.js index 2515069..a5bceb9 100644 --- a/remoting/webapp/crd/js/video_frame_recorder.js +++ b/remoting/webapp/crd/js/video_frame_recorder.js @@ -63,8 +63,8 @@ remoting.VideoFrameRecorder.prototype.handleMessage = return false; } - var messageType = getStringAttr(message, 'type'); - var messageData = getStringAttr(message, 'data'); + var messageType = base.getStringAttr(message, 'type'); + var messageData = base.getStringAttr(message, 'data'); if (messageType == 'next-frame-reply') { if (!this.fileWriter_) { diff --git a/remoting/webapp/js_proto/qunit_proto.js b/remoting/webapp/js_proto/qunit_proto.js index 2b2816c1..aed670e 100644 --- a/remoting/webapp/js_proto/qunit_proto.js +++ b/remoting/webapp/js_proto/qunit_proto.js @@ -69,6 +69,20 @@ QUnit.Assert.prototype.equal = function(a, b, opt_message) {}; 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 {{ * beforeEach: (function(QUnit.Assert=) | undefined), * afterEach: (function(QUnit.Assert=) | undefined) |