X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fremoting%2Fwebapp%2Fclient_plugin.js;h=824098bf5bfd07734a9b3e94a4b34b253969d40f;hb=ff3e2503a20db9193d323c1d19c38c68004dec4a;hp=cc621f2cf35ba5206e4ec76a790f718e36560394;hpb=7338fba38ba696536d1cc9d389afd716a6ab2fe6;p=platform%2Fframework%2Fweb%2Fcrosswalk.git diff --git a/src/remoting/webapp/client_plugin.js b/src/remoting/webapp/client_plugin.js index cc621f2..824098b 100644 --- a/src/remoting/webapp/client_plugin.js +++ b/src/remoting/webapp/client_plugin.js @@ -18,10 +18,14 @@ var remoting = remoting || {}; /** * @param {remoting.ViewerPlugin} plugin The plugin embed element. + * @param {function(string, string):boolean} onExtensionMessage The handler for + * protocol extension messages. Returns true if a message is recognized; + * false otherwise. * @constructor */ -remoting.ClientPlugin = function(plugin) { +remoting.ClientPlugin = function(plugin, onExtensionMessage) { this.plugin = plugin; + this.onExtensionMessage_ = onExtensionMessage; this.desktopWidth = 0; this.desktopHeight = 0; @@ -39,6 +43,7 @@ remoting.ClientPlugin = function(plugin) { this.onConnectionStatusUpdateHandler = function(state, error) {}; /** @param {boolean} ready Connection ready state. */ this.onConnectionReadyHandler = function(ready) {}; + /** * @param {string} tokenUrl Token-request URL, received from the host. * @param {string} hostPublicKey Public key for the host. @@ -51,6 +56,9 @@ remoting.ClientPlugin = function(plugin) { this.onSetCapabilitiesHandler = function (capabilities) {}; this.fetchPinHandler = function (supportsPairing) {}; + /** @type {remoting.MediaSourceRenderer} */ + this.mediaSourceRenderer_ = null; + /** @type {number} */ this.pluginApiVersion_ = -1; /** @type {Array.} */ @@ -93,7 +101,8 @@ remoting.ClientPlugin.Feature = { THIRD_PARTY_AUTH: 'thirdPartyAuth', TRAP_KEY: 'trapKey', PINLESS_AUTH: 'pinlessAuth', - EXTENSION_MESSAGE: 'extensionMessage' + EXTENSION_MESSAGE: 'extensionMessage', + MEDIA_SOURCE_RENDERING: 'mediaSourceRendering' }; /** @@ -117,17 +126,34 @@ remoting.ClientPlugin.prototype.API_VERSION_ = 6; remoting.ClientPlugin.prototype.API_MIN_VERSION_ = 5; /** - * @param {string} messageStr Message from the plugin. + * @param {string|{method:string, data:Object.}} + * rawMessage Message from the plugin. + * @private */ -remoting.ClientPlugin.prototype.handleMessage_ = function(messageStr) { - var message = /** @type {{method:string, data:Object.}} */ - jsonParseSafe(messageStr); +remoting.ClientPlugin.prototype.handleMessage_ = function(rawMessage) { + var message = + /** @type {{method:string, data:Object.}} */ + ((typeof(rawMessage) == 'string') ? jsonParseSafe(rawMessage) + : rawMessage); if (!message || !('method' in message) || !('data' in message)) { - console.error('Received invalid message from the plugin: ' + messageStr); + console.error('Received invalid message from the plugin:', rawMessage); return; } + try { + this.handleMessageMethod_(message); + } catch(e) { + console.error(/** @type {*} */ (e)); + } +} + +/** + * @param {{method:string, data:Object.}} + * message Parsed message from the plugin. + * @private + */ +remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) { /** * Splits a string into a list of words delimited by spaces. * @param {string} str String that should be split. @@ -143,41 +169,27 @@ remoting.ClientPlugin.prototype.handleMessage_ = function(messageStr) { // Reset the size in case we had to enlarge it to support click-to-play. this.plugin.width = 0; this.plugin.height = 0; - if (typeof message.data['apiVersion'] != 'number' || - typeof message.data['apiMinVersion'] != 'number') { - console.error('Received invalid hello message: ' + messageStr); - return; - } - this.pluginApiVersion_ = /** @type {number} */ message.data['apiVersion']; + this.pluginApiVersion_ = getNumberAttr(message.data, 'apiVersion'); + this.pluginApiMinVersion_ = getNumberAttr(message.data, 'apiMinVersion'); if (this.pluginApiVersion_ >= 7) { - if (typeof message.data['apiFeatures'] != 'string') { - console.error('Received invalid hello message: ' + messageStr); - return; - } this.pluginApiFeatures_ = - /** @type {Array.} */ tokenize(message.data['apiFeatures']); + tokenize(getStringAttr(message.data, 'apiFeatures')); // Negotiate capabilities. /** @type {!Array.} */ var requestedCapabilities = []; if ('requestedCapabilities' in message.data) { - if (typeof message.data['requestedCapabilities'] != 'string') { - console.error('Received invalid hello message: ' + messageStr); - return; - } - requestedCapabilities = tokenize(message.data['requestedCapabilities']); + requestedCapabilities = + tokenize(getStringAttr(message.data, 'requestedCapabilities')); } /** @type {!Array.} */ var supportedCapabilities = []; if ('supportedCapabilities' in message.data) { - if (typeof message.data['supportedCapabilities'] != 'string') { - console.error('Received invalid hello message: ' + messageStr); - return; - } - supportedCapabilities = tokenize(message.data['supportedCapabilities']); + supportedCapabilities = + tokenize(getStringAttr(message.data, 'supportedCapabilities')); } // At the moment the webapp does not recognize any of @@ -200,151 +212,117 @@ remoting.ClientPlugin.prototype.handleMessage_ = function(messageStr) { } else { this.pluginApiFeatures_ = ['highQualityScaling']; } - this.pluginApiMinVersion_ = - /** @type {number} */ message.data['apiMinVersion']; this.helloReceived_ = true; if (this.onInitializedCallback_ != null) { this.onInitializedCallback_(true); this.onInitializedCallback_ = null; } + } else if (message.method == 'sendOutgoingIq') { - if (typeof message.data['iq'] != 'string') { - console.error('Received invalid sendOutgoingIq message: ' + messageStr); - return; - } - this.onOutgoingIqHandler(message.data['iq']); - } else if (message.method == 'logDebugMessage') { - if (typeof message.data['message'] != 'string') { - console.error('Received invalid logDebugMessage message: ' + messageStr); - return; - } - this.onDebugMessageHandler(message.data['message']); - } else if (message.method == 'onConnectionStatus') { - if (typeof message.data['state'] != 'string' || - !remoting.ClientSession.State.hasOwnProperty(message.data['state']) || - typeof message.data['error'] != 'string') { - console.error('Received invalid onConnectionState message: ' + - messageStr); - return; - } + this.onOutgoingIqHandler(getStringAttr(message.data, 'iq')); - /** @type {remoting.ClientSession.State} */ - var state = remoting.ClientSession.State[message.data['state']]; - var error; - if (remoting.ClientSession.ConnectionError.hasOwnProperty( - message.data['error'])) { - error = /** @type {remoting.ClientSession.ConnectionError} */ - remoting.ClientSession.ConnectionError[message.data['error']]; - } else { - error = remoting.ClientSession.ConnectionError.UNKNOWN; - } + } else if (message.method == 'logDebugMessage') { + this.onDebugMessageHandler(getStringAttr(message.data, 'message')); + } else if (message.method == 'onConnectionStatus') { + var state = remoting.ClientSession.State.fromString( + getStringAttr(message.data, 'state')) + var error = remoting.ClientSession.ConnectionError.fromString( + getStringAttr(message.data, 'error')); this.onConnectionStatusUpdateHandler(state, error); + } else if (message.method == 'onDesktopSize') { - if (typeof message.data['width'] != 'number' || - typeof message.data['height'] != 'number') { - console.error('Received invalid onDesktopSize message: ' + messageStr); - return; - } - this.desktopWidth = /** @type {number} */ message.data['width']; - this.desktopHeight = /** @type {number} */ message.data['height']; - this.desktopXDpi = (typeof message.data['x_dpi'] == 'number') ? - /** @type {number} */ (message.data['x_dpi']) : 96; - this.desktopYDpi = (typeof message.data['y_dpi'] == 'number') ? - /** @type {number} */ (message.data['y_dpi']) : 96; + this.desktopWidth = getNumberAttr(message.data, 'width'); + this.desktopHeight = getNumberAttr(message.data, 'height'); + this.desktopXDpi = getNumberAttr(message.data, 'x_dpi', 96); + this.desktopYDpi = getNumberAttr(message.data, 'y_dpi', 96); this.onDesktopSizeUpdateHandler(); + } else if (message.method == 'onPerfStats') { - if (typeof message.data['videoBandwidth'] != 'number' || - typeof message.data['videoFrameRate'] != 'number' || - typeof message.data['captureLatency'] != 'number' || - typeof message.data['encodeLatency'] != 'number' || - typeof message.data['decodeLatency'] != 'number' || - typeof message.data['renderLatency'] != 'number' || - typeof message.data['roundtripLatency'] != 'number') { - console.error('Received incorrect onPerfStats message: ' + messageStr); - return; - } + // Return value is ignored. These calls will throw an error if the value + // is not a number. + getNumberAttr(message.data, 'videoBandwidth'); + getNumberAttr(message.data, 'videoFrameRate'); + getNumberAttr(message.data, 'captureLatency'); + getNumberAttr(message.data, 'encodeLatency'); + getNumberAttr(message.data, 'decodeLatency'); + getNumberAttr(message.data, 'renderLatency'); + getNumberAttr(message.data, 'roundtripLatency'); this.perfStats_ = /** @type {remoting.ClientSession.PerfStats} */ message.data; + } else if (message.method == 'injectClipboardItem') { - if (typeof message.data['mimeType'] != 'string' || - typeof message.data['item'] != 'string') { - console.error('Received incorrect injectClipboardItem message.'); - return; - } + var mimetype = getStringAttr(message.data, 'mimeType'); + var item = getStringAttr(message.data, 'item'); if (remoting.clipboard) { - remoting.clipboard.fromHost(message.data['mimeType'], - message.data['item']); + remoting.clipboard.fromHost(mimetype, item); } + } else if (message.method == 'onFirstFrameReceived') { if (remoting.clientSession) { remoting.clientSession.onFirstFrameReceived(); } + } else if (message.method == 'onConnectionReady') { - if (typeof message.data['ready'] != 'boolean') { - console.error('Received incorrect onConnectionReady message.'); - return; - } - var ready = /** @type {boolean} */ message.data['ready']; + var ready = getBooleanAttr(message.data, 'ready'); this.onConnectionReadyHandler(ready); + } else if (message.method == 'fetchPin') { // The pairingSupported value in the dictionary indicates whether both // client and host support pairing. If the client doesn't support pairing, // then the value won't be there at all, so give it a default of false. - /** @type {boolean} */ - var pairingSupported = false; - if ('pairingSupported' in message.data) { - pairingSupported = - /** @type {boolean} */ message.data['pairingSupported']; - if (typeof pairingSupported != 'boolean') { - console.error('Received incorrect fetchPin message.'); - return; - } - } + var pairingSupported = getBooleanAttr(message.data, 'pairingSupported', + false) this.fetchPinHandler(pairingSupported); - } else if (message.method == 'setCapabilities') { - if (typeof message.data['capabilities'] != 'string') { - console.error('Received incorrect setCapabilities message.'); - return; - } + } else if (message.method == 'setCapabilities') { /** @type {!Array.} */ - var capabilities = tokenize(message.data['capabilities']); + var capabilities = tokenize(getStringAttr(message.data, 'capabilities')); this.onSetCapabilitiesHandler(capabilities); + } else if (message.method == 'fetchThirdPartyToken') { - if (typeof message.data['tokenUrl'] != 'string' || - typeof message.data['hostPublicKey'] != 'string' || - typeof message.data['scope'] != 'string') { - console.error('Received incorrect fetchThirdPartyToken message.'); - return; - } - var tokenUrl = /** @type {string} */ message.data['tokenUrl']; - var hostPublicKey = - /** @type {string} */ message.data['hostPublicKey']; - var scope = /** @type {string} */ message.data['scope']; + var tokenUrl = getStringAttr(message.data, 'tokenUrl'); + var hostPublicKey = getStringAttr(message.data, 'hostPublicKey'); + var scope = getStringAttr(message.data, 'scope'); this.fetchThirdPartyTokenHandler(tokenUrl, hostPublicKey, scope); + } else if (message.method == 'pairingResponse') { - var clientId = /** @type {string} */ message.data['clientId']; - var sharedSecret = /** @type {string} */ message.data['sharedSecret']; - if (typeof clientId != 'string' || typeof sharedSecret != 'string') { - console.error('Received incorrect pairingResponse message.'); - return; - } + var clientId = getStringAttr(message.data, 'clientId'); + var sharedSecret = getStringAttr(message.data, 'sharedSecret'); this.onPairingComplete_(clientId, sharedSecret); + } else if (message.method == 'extensionMessage') { - if (typeof(message.data['type']) != 'string' || - typeof(message.data['data']) != 'string') { - console.error('Invalid extension message:', message.data); - return; - } - switch (message.data['type']) { + var extMsgType = getStringAttr(message, 'type'); + var extMsgData = getStringAttr(message, 'data'); + switch (extMsgType) { case 'test-echo-reply': - console.log('Got echo reply: ' + message.data['data']); + console.log('Got echo reply: ' + extMsgData); break; default: - console.log('Unexpected message received: ' + - message.data['type'] + ': ' + message.data['data']); + if (!this.onExtensionMessage_(extMsgType, extMsgData)) { + console.log('Unexpected message received: ' + + extMsgType + ': ' + extMsgData); + } } + + } else if (message.method == 'mediaSourceReset') { + if (!this.mediaSourceRenderer_) { + console.error('Unexpected mediaSourceReset.'); + return; + } + this.mediaSourceRenderer_.reset(getStringAttr(message.data, 'format')) + + } else if (message.method == 'mediaSourceData') { + if (!(message.data['buffer'] instanceof ArrayBuffer)) { + console.error('Invalid mediaSourceData message:', message.data); + return; + } + if (!this.mediaSourceRenderer_) { + console.error('Unexpected mediaSourceData.'); + return; + } + this.mediaSourceRenderer_.onIncomingData( + (/** @type {ArrayBuffer} */ message.data['buffer'])); } }; @@ -558,8 +536,9 @@ remoting.ClientPlugin.prototype.notifyClientResolution = */ remoting.ClientPlugin.prototype.pauseVideo = function(pause) { - if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_VIDEO)) + if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_VIDEO)) { return; + } this.plugin.postMessage(JSON.stringify( { method: 'pauseVideo', data: { pause: pause }})); }; @@ -571,8 +550,9 @@ remoting.ClientPlugin.prototype.pauseVideo = */ remoting.ClientPlugin.prototype.pauseAudio = function(pause) { - if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_AUDIO)) + if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_AUDIO)) { return; + } this.plugin.postMessage(JSON.stringify( { method: 'pauseAudio', data: { pause: pause }})); }; @@ -651,6 +631,21 @@ remoting.ClientPlugin.prototype.sendClientMessage = }; /** + * Request MediaStream-based rendering. + * + * @param {remoting.MediaSourceRenderer} mediaSourceRenderer + */ +remoting.ClientPlugin.prototype.enableMediaSourceRendering = + function(mediaSourceRenderer) { + if (!this.hasFeature(remoting.ClientPlugin.Feature.MEDIA_SOURCE_RENDERING)) { + return; + } + this.mediaSourceRenderer_ = mediaSourceRenderer; + this.plugin.postMessage(JSON.stringify( + { method: 'enableMediaSourceRendering', data: {} })); +}; + +/** * If we haven't yet received a "hello" message from the plugin, change its * size so that the user can confirm it if click-to-play is enabled, or can * see the "this plugin is disabled" message if it is actually disabled.