summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorjrw <jrw@chromium.org>2015-03-19 13:08:45 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-19 20:09:52 +0000
commitb814ba0d3921aaf296ebe1b653ab2827f671a7dd (patch)
treedc9c27a220e23b9aa18603de67c33eeb50212b2e /remoting
parent43dc9613e9badb5e0c6a11d25106271d2f23899f (diff)
downloadchromium_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.gypi1
-rw-r--r--remoting/webapp/app_remoting/js/app_remoting.js14
-rw-r--r--remoting/webapp/base/js/base.js1
-rw-r--r--remoting/webapp/crd/js/cast_extension_handler.js2
-rw-r--r--remoting/webapp/crd/js/client_plugin_host_desktop_impl.js10
-rw-r--r--remoting/webapp/crd/js/client_plugin_impl.js70
-rw-r--r--remoting/webapp/crd/js/gnubby_auth_handler.js8
-rw-r--r--remoting/webapp/crd/js/host.js10
-rw-r--r--remoting/webapp/crd/js/host_daemon_facade.js39
-rw-r--r--remoting/webapp/crd/js/host_list_api_impl.js20
-rw-r--r--remoting/webapp/crd/js/it2me_host_facade.js17
-rw-r--r--remoting/webapp/crd/js/typecheck.js241
-rw-r--r--remoting/webapp/crd/js/typecheck_unittest.js240
-rw-r--r--remoting/webapp/crd/js/video_frame_recorder.js4
-rw-r--r--remoting/webapp/js_proto/qunit_proto.js14
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)