| Index: remoting/webapp/cast_extension_handler.js
|
| diff --git a/remoting/webapp/cast_extension_handler.js b/remoting/webapp/cast_extension_handler.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a0c38033a388df11cfbf842168a9c2a8af1f0016
|
| --- /dev/null
|
| +++ b/remoting/webapp/cast_extension_handler.js
|
| @@ -0,0 +1,333 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +/**
|
| + * @fileoverview Description of this file.
|
| + * Class handling interaction with the cast extension session of the Chromoting
|
| + * host. It receives and sends extension messages from/to the host through
|
| + * the client session. It uses the Google Cast Chrome Sender API library to
|
| + * interact with nearby Cast receivers.
|
| + *
|
| + * Once it establishes connection with a Cast device (upon user choice), it
|
| + * creates a session loads our registered receiver application and then becomes
|
| + * a message proxy between the host and cast device, helping negotiate
|
| + * their peer connection.
|
| + */
|
| +
|
| +'use strict';
|
| +
|
| +/** @suppress {duplicate} */
|
| +var remoting = remoting || {};
|
| +
|
| +/**
|
| + * @constructor
|
| + * @param {!remoting.ClientSession} clientSession The client session to send
|
| + * cast extension messages to.
|
| + */
|
| +remoting.CastExtensionHandler = function(clientSession) {
|
| + this.clientSession_ = clientSession;
|
| +
|
| + /** @type {chrome.cast.Session}
|
| + * @private */
|
| + this.session = null;
|
| +
|
| + /** @type {string}
|
| + * @private */
|
| + this.chromotingNamespace = 'urn:x-cast:com.chromoting.cast.all';
|
| +
|
| + /** @type {Array}
|
| + * @private */
|
| + this.messageQueue = [];
|
| +
|
| + this.start();
|
| +};
|
| +
|
| +/**
|
| + * The id of the script node.
|
| + * @type {string}
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.SCRIPT_NODE_ID_ = 'cast-script-node';
|
| +
|
| +/**
|
| + * Attempts to load the Google Cast Chrome Sender API libary.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.start = function() {
|
| + var node = document.getElementById(this.SCRIPT_NODE_ID_);
|
| + if (node) {
|
| + console.error(
|
| + '### Multiple calls to CastExtensionHandler.start not expected.');
|
| + return;
|
| + }
|
| +
|
| + // Create a script node to load the Cast Sender API.
|
| + node = document.createElement('script');
|
| + node.id = this.SCRIPT_NODE_ID_;
|
| + node.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js";
|
| + node.type = 'text/javascript';
|
| + document.body.insertBefore(node, document.body.firstChild);
|
| +
|
| + /** @type {remoting.CastExtensionHandler} */
|
| + var that = this;
|
| + var onLoad = function() {
|
| + window['__onGCastApiAvailable'] = that.onGCastApiAvailable.bind(that);
|
| +
|
| + };
|
| + var onLoadError = function(event) {
|
| + console.error("### Failed to load cast_sender.js.");
|
| + }
|
| + node.addEventListener('load', onLoad, false);
|
| + node.addEventListener('error', onLoadError, false);
|
| +
|
| +};
|
| +
|
| +/**
|
| + * Process cast-host-extension messages.
|
| + * @param {string} msgString The cast host extension message data.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onMessage = function(msgString) {
|
| + var message = getJsonObjectFromString(msgString);
|
| + console.log("### Received Message from Host: ", message);
|
| + // Save messages to send after a session is established.
|
| + this.messageQueue.push(message);
|
| + // Trigger the sending of pending messages, followed by the one just
|
| + // received.
|
| + if(this.session) {
|
| + this.sendPendingMessages();
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Send cast-extension messages through the client session.
|
| + * @param {Object} response The JSON response to be sent to the host. The
|
| + * response object must contain the appropriate keys.
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.sendMessageToHost_ =
|
| + function(response) {
|
| + this.clientSession_.sendCastExtensionMessage(response);
|
| +};
|
| +
|
| +/**
|
| + * Send pending messages from the host to the receiver app.
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.sendPendingMessages = function() {
|
| + var len = this.messageQueue.length;
|
| + for(var i = 0; i<len; i++) {
|
| + this.session.sendMessage(this.chromotingNamespace,
|
| + this.messageQueue[i],
|
| + this.sendMessageSuccess.bind(this),
|
| + this.sendMessageFailure.bind(this));
|
| + }
|
| + this.messageQueue = [];
|
| +};
|
| +
|
| +/**
|
| + * Event handler for '__onGCastApiAvailable' window event.
|
| + *
|
| + * @param {boolean} loaded Represents if the API loaded succesfully.
|
| + * @param {Object} errorInfo Info if the API load failed.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onGCastApiAvailable =
|
| + function(loaded, errorInfo) {
|
| + if (loaded) {
|
| + this.initializeCastApi();
|
| + } else {
|
| + console.log(errorInfo);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Initialize the Cast API.
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.initializeCastApi = function() {
|
| + var applicationID = "8A1211E3";
|
| + var sessionRequest = new chrome.cast.SessionRequest(applicationID);
|
| + var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
|
| + this.sessionListener.bind(this),
|
| + this.receiverListener.bind(this));
|
| + chrome.cast.initialize(apiConfig,
|
| + this.onInitSuccess.bind(this),
|
| + this.onInitError.bind(this),
|
| + chrome.cast.AutoJoinPolicy.PAGE_SCOPED,
|
| + chrome.cast.DefaulActionPolicy.CREATE_SESSION);
|
| +};
|
| +
|
| +/**
|
| + * Callback for successful initialization of the Cast API.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onInitSuccess = function() {
|
| + console.log("### Initialization Successful.");
|
| +};
|
| +
|
| +/**
|
| + * Callback for failed initialization of the Cast API.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onInitError = function() {
|
| + console.log("### Initialization Failed.");
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when a session is created or connected by the SDK.
|
| + * Note: The requestSession method would not cause this callback to be invoked
|
| + * since it is passed its own listener.
|
| + * @param {chrome.cast.Session} e The resulting session, non-nullable.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.sessionListener = function(e) {
|
| + console.log('### New Session. ID: ' + /** @type {string} */ (e.sessionId));
|
| + this.session = e;
|
| + if(this.session.media.length != 0) {
|
| + console.log("### Found " + this.session.media.length + " sessions.");
|
| + this.onMediaDiscovered('sessionListener', this.session.media[0]);
|
| + }
|
| + this.session.addMediaListener(
|
| + this.onMediaDiscovered.bind(this, 'addMediaListener'));
|
| + this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
|
| + this.session.addMessageListener(this.chromotingNamespace,
|
| + this.chromotingMessageListener.bind(this));
|
| + this.session.sendMessage(this.chromotingNamespace,
|
| + {subject : 'test', chromoting_data : 'Hello, Cast.'},
|
| + this.sendMessageSuccess.bind(this),
|
| + this.sendMessageFailure.bind(this));
|
| + this.sendPendingMessages();
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when a media session is created by another sender.
|
| + * @param {string} how How this callback was triggered.
|
| + * @param {chrome.cast.media.Media} media The media item discovered.
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onMediaDiscovered =
|
| + function(how, media) {
|
| + console.log('### New Media Session ID: ' + media.mediaSessionId);
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when a cast extension message was sent to the cast device
|
| + * successfully.
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.sendMessageSuccess = function() {
|
| + console.log('### Sent Message Successfully.');
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when a cast extension message failed to be sent to the cast
|
| + * device.
|
| + * @param {Object} error The error.
|
| + * @private
|
| + */
|
| +remoting.CastExtensionHandler.prototype.sendMessageFailure = function(error) {
|
| + console.error('### Failed to Send Message.', error);
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when a cast extension message was sent to the cast device
|
| + * successfully.
|
| + * @param {string} ns The namespace of the message received.
|
| + * @param {string} message The stringified JSON message received.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.chromotingMessageListener =
|
| + function(ns, message) {
|
| + var messageObj = getJsonObjectFromString(message);
|
| + console.log("### Received Message from Cast: " + ns + "-" + messageObj);
|
| + this.sendMessageToHost_(messageObj);
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when there updates to the current session.
|
| + *
|
| + * @param {boolean} isAlive True if the session is still alive.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.sessionUpdateListener =
|
| + function(isAlive) {
|
| + var message = isAlive ? '### Session Updated' : '### Session Removed';
|
| + message += ': ' + this.session.sessionId +'.';
|
| + console.log(message);
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when the availability of a Cast receiver that supports
|
| + * the application in sessionRequest is known or changes.
|
| + *
|
| + * @param {chrome.cast.ReceiverAvailability} e Describes receiver availability.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.receiverListener = function(e) {
|
| + if( e === chrome.cast.ReceiverAvailability.AVAILABLE) {
|
| + console.log("### Receiver(s) Found.");
|
| + }
|
| + else {
|
| + console.log("### No Receivers Available.");
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Launches the associated receiver application by requesting that it be created
|
| + * or joined on the Cast device. By default, the SessionRequest passed during
|
| + * initialization is used.
|
| + * Note: This method is intended to be used as a click listener for a custom
|
| + * cast button on the webpage. We currently use the default cast button in
|
| + * Chrome, so this method is unused.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.launchApp = function() {
|
| + console.log("### Launching Cast App.");
|
| + chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this),
|
| + this.onLaunchError.bind(this));
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when the chrome.cast.requestSession is successful.
|
| + *
|
| + * @param {chrome.cast.Session} e The requested session.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onRequestSessionSuccess = function (e) {
|
| + console.log("### Successfully created session: " + e.sessionId);
|
| + this.session = e;
|
| + this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
|
| + if(this.session.media.length != 0) {
|
| + this.onMediaDiscovered('onRequestSession', this.session.media[0]);
|
| + }
|
| + this.session.addMediaListener(
|
| + this.onMediaDiscovered.bind(this, 'addMediaListener'));
|
| + this.session.addMessageListener(this.chromotingNamespace,
|
| + this.chromotingMessageListener.bind(this));
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when the launchApp() fails.
|
| + * @param {chrome.cast.Error} error The error code.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onLaunchError = function(error) {
|
| + console.error("### Error Casting to Receiver.", error);
|
| +};
|
| +
|
| +/**
|
| + * Stops the running receiver application associated with the session.
|
| + * TODO(aiguha): When the user disconnects using the blue drop down bar,
|
| + * the client session should notify the CastExtensionHandler, which should
|
| + * call this method to close the session with the Cast device.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.stopApp = function() {
|
| + this.session.stop(this.onStopAppSuccess.bind(this),
|
| + this.onStopAppError.bind(this));
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when the receiver application is stopped successfully.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onStopAppSuccess = function() {
|
| + console.log('### Session Stopped.');
|
| +};
|
| +
|
| +/**
|
| + * Listener invoked when we fail to stop the receiver application.
|
| + *
|
| + * @param {chrome.cast.Error} error The error code.
|
| + */
|
| +remoting.CastExtensionHandler.prototype.onStopAppError = function(error) {
|
| + console.error('### Error Stopping App: ', error);
|
| +};
|
|
|