Commit 2f4e73e9 authored by aiguha@chromium.org's avatar aiguha@chromium.org

The CastExtensionHandler handles interaction with the Cast Host Extension of

the chromoting host.
It only modifies webapp behavior if the "casting" capability is part of the
negotiated set between host and client.

It performs the following tasks:
1. Sends and receives extension messages to/from the host.
2. Initializes and uses the Google Cast Chrome Sender API library to interact
with nearby Cast Receivers, acting as a Sender App.
3. Acts as a message proxy between the Cast Host Extension and the Cast
Receiver, brokering their peer connection.

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

Cr-Commit-Position: refs/heads/master@{#290140}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290140 0039d316-1c4b-4281-b951-d872f2087c98
parent 23935931
...@@ -122,6 +122,10 @@ ...@@ -122,6 +122,10 @@
'remoting_webapp_js_gnubby_auth_files': [ 'remoting_webapp_js_gnubby_auth_files': [
'webapp/gnubby_auth_handler.js', 'webapp/gnubby_auth_handler.js',
], ],
# cast extension handler JavaScript files.
'remoting_webapp_js_cast_extension_files': [
'webapp/cast_extension_handler.js',
],
# browser test JavaScript files. # browser test JavaScript files.
'remoting_webapp_js_browser_test_files': [ 'remoting_webapp_js_browser_test_files': [
'webapp/browser_test/browser_test.js', 'webapp/browser_test/browser_test.js',
...@@ -164,6 +168,7 @@ ...@@ -164,6 +168,7 @@
'<@(remoting_webapp_js_auth_google_files)', '<@(remoting_webapp_js_auth_google_files)',
'<@(remoting_webapp_js_client_files)', '<@(remoting_webapp_js_client_files)',
'<@(remoting_webapp_js_gnubby_auth_files)', '<@(remoting_webapp_js_gnubby_auth_files)',
'<@(remoting_webapp_js_cast_extension_files)',
'<@(remoting_webapp_js_host_files)', '<@(remoting_webapp_js_host_files)',
'<@(remoting_webapp_js_logging_files)', '<@(remoting_webapp_js_logging_files)',
'<@(remoting_webapp_js_ui_files)', '<@(remoting_webapp_js_ui_files)',
......
This diff is collapsed.
...@@ -67,6 +67,9 @@ remoting.ClientPlugin = function(container, onExtensionMessage) { ...@@ -67,6 +67,9 @@ remoting.ClientPlugin = function(container, onExtensionMessage) {
*/ */
this.updateMouseCursorImage = function(url, hotspotX, hotspotY) {}; this.updateMouseCursorImage = function(url, hotspotX, hotspotY) {};
/** @param {string} data Remote cast extension message. */
this.onCastExtensionHandler = function(data) {};
/** @type {remoting.MediaSourceRenderer} */ /** @type {remoting.MediaSourceRenderer} */
this.mediaSourceRenderer_ = null; this.mediaSourceRenderer_ = null;
...@@ -263,6 +266,13 @@ remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) { ...@@ -263,6 +266,13 @@ remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) {
// Let the host know that we can use the video framerecording extension. // Let the host know that we can use the video framerecording extension.
this.capabilities_.push( this.capabilities_.push(
remoting.ClientSession.Capability.VIDEO_RECORDER); remoting.ClientSession.Capability.VIDEO_RECORDER);
// Let the host know that we can support casting of the screen.
// TODO(aiguha): Add this capability based on a gyp/command-line flag,
// rather than by default.
this.capabilities_.push(
remoting.ClientSession.Capability.CAST);
} else if (this.pluginApiVersion_ >= 6) { } else if (this.pluginApiVersion_ >= 6) {
this.pluginApiFeatures_ = ['highQualityScaling', 'injectKeyEvent']; this.pluginApiFeatures_ = ['highQualityScaling', 'injectKeyEvent'];
} else { } else {
...@@ -357,6 +367,9 @@ remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) { ...@@ -357,6 +367,9 @@ remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) {
case 'test-echo-reply': case 'test-echo-reply':
console.log('Got echo reply: ' + extMsgData); console.log('Got echo reply: ' + extMsgData);
break; break;
case 'cast_message':
this.onCastExtensionHandler(extMsgData);
break;
default: default:
if (!this.onExtensionMessage_(extMsgType, extMsgData)) { if (!this.onExtensionMessage_(extMsgType, extMsgData)) {
console.log('Unexpected message received: ' + console.log('Unexpected message received: ' +
......
...@@ -22,6 +22,13 @@ ...@@ -22,6 +22,13 @@
/** @suppress {duplicate} */ /** @suppress {duplicate} */
var remoting = remoting || {}; var remoting = remoting || {};
/**
* True if Cast capability is supported.
*
* @type {boolean}
*/
remoting.enableCast = false;
/** /**
* @param {HTMLElement} container Container element for the client view. * @param {HTMLElement} container Container element for the client view.
* @param {string} hostDisplayName A human-readable name for the host. * @param {string} hostDisplayName A human-readable name for the host.
...@@ -166,6 +173,9 @@ remoting.ClientSession = function(container, hostDisplayName, accessCode, ...@@ -166,6 +173,9 @@ remoting.ClientSession = function(container, hostDisplayName, accessCode,
/** @type {remoting.GnubbyAuthHandler} @private */ /** @type {remoting.GnubbyAuthHandler} @private */
this.gnubbyAuthHandler_ = null; this.gnubbyAuthHandler_ = null;
/** @type {remoting.CastExtensionHandler} @private */
this.castExtensionHandler_ = null;
if (this.mode_ == remoting.ClientSession.Mode.IT2ME) { if (this.mode_ == remoting.ClientSession.Mode.IT2ME) {
// Resize-to-client is not supported for IT2Me hosts. // Resize-to-client is not supported for IT2Me hosts.
this.resizeToClientButton_.hidden = true; this.resizeToClientButton_.hidden = true;
...@@ -366,7 +376,8 @@ remoting.ClientSession.Capability = { ...@@ -366,7 +376,8 @@ remoting.ClientSession.Capability = {
// this.plugin_.notifyClientResolution(). // this.plugin_.notifyClientResolution().
SEND_INITIAL_RESOLUTION: 'sendInitialResolution', SEND_INITIAL_RESOLUTION: 'sendInitialResolution',
RATE_LIMIT_RESIZE_REQUESTS: 'rateLimitResizeRequests', RATE_LIMIT_RESIZE_REQUESTS: 'rateLimitResizeRequests',
VIDEO_RECORDER: 'videoRecorder' VIDEO_RECORDER: 'videoRecorder',
CAST: 'casting'
}; };
/** /**
...@@ -548,6 +559,8 @@ remoting.ClientSession.prototype.onPluginInitialized_ = function(initialized) { ...@@ -548,6 +559,8 @@ remoting.ClientSession.prototype.onPluginInitialized_ = function(initialized) {
this.plugin_.onSetCapabilitiesHandler = this.onSetCapabilities_.bind(this); this.plugin_.onSetCapabilitiesHandler = this.onSetCapabilities_.bind(this);
this.plugin_.onGnubbyAuthHandler = this.processGnubbyAuthMessage_.bind(this); this.plugin_.onGnubbyAuthHandler = this.processGnubbyAuthMessage_.bind(this);
this.plugin_.updateMouseCursorImage = this.updateMouseCursorImage_.bind(this); this.plugin_.updateMouseCursorImage = this.updateMouseCursorImage_.bind(this);
this.plugin_.onCastExtensionHandler =
this.processCastExtensionMessage_.bind(this);
this.initiateConnection_(); this.initiateConnection_();
}; };
...@@ -1070,6 +1083,7 @@ remoting.ClientSession.prototype.setState_ = function(newState) { ...@@ -1070,6 +1083,7 @@ remoting.ClientSession.prototype.setState_ = function(newState) {
this.logToServer.logClientSessionStateChange(state, this.error_, this.mode_); this.logToServer.logClientSessionStateChange(state, this.error_, this.mode_);
if (this.state_ == remoting.ClientSession.State.CONNECTED) { if (this.state_ == remoting.ClientSession.State.CONNECTED) {
this.createGnubbyAuthHandler_(); this.createGnubbyAuthHandler_();
this.createCastExtensionHandler_();
} }
this.raiseEvent(remoting.ClientSession.Events.stateChanged, this.raiseEvent(remoting.ClientSession.Events.stateChanged,
...@@ -1539,3 +1553,43 @@ remoting.ClientSession.prototype.getPluginPositionForTesting = function() { ...@@ -1539,3 +1553,43 @@ remoting.ClientSession.prototype.getPluginPositionForTesting = function() {
left: parseFloat(style.marginLeft) left: parseFloat(style.marginLeft)
}; };
}; };
/**
* Send a Cast extension message to the host.
* @param {Object} data The cast message data.
*/
remoting.ClientSession.prototype.sendCastExtensionMessage = function(data) {
if (!this.plugin_)
return;
this.plugin_.sendClientMessage('cast_message', JSON.stringify(data));
};
/**
* Process a remote Cast extension message from the host.
* @param {string} data Remote cast extension data message.
* @private
*/
remoting.ClientSession.prototype.processCastExtensionMessage_ = function(data) {
if (this.castExtensionHandler_) {
try {
this.castExtensionHandler_.onMessage(data);
} catch (err) {
console.error('Failed to process cast message: ',
/** @type {*} */ (err));
}
} else {
console.error('Received unexpected cast message');
}
};
/**
* Create a CastExtensionHandler and inform the host that cast extension
* is supported.
* @private
*/
remoting.ClientSession.prototype.createCastExtensionHandler_ = function() {
if (remoting.enableCast && this.mode_ == remoting.ClientSession.Mode.ME2ME) {
this.castExtensionHandler_ = new remoting.CastExtensionHandler(this);
}
};
...@@ -385,3 +385,126 @@ function ClientRect() { ...@@ -385,3 +385,126 @@ function ClientRect() {
/** @type {number} */ /** @type {number} */
this.right = 0; this.right = 0;
}; };
/** @type {Object} */
chrome.cast = {};
/** @constructor */
chrome.cast.AutoJoinPolicy = function() {};
/** @type {chrome.cast.AutoJoinPolicy} */
chrome.cast.AutoJoinPolicy.PAGE_SCOPED;
/** @type {chrome.cast.AutoJoinPolicy} */
chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED;
/** @type {chrome.cast.AutoJoinPolicy} */
chrome.cast.AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED;
/** @constructor */
chrome.cast.DefaultActionPolicy = function() {};
/** @type {chrome.cast.DefaultActionPolicy} */
chrome.cast.DefaultActionPolicy.CAST_THIS_TAB;
/** @type {chrome.cast.DefaultActionPolicy} */
chrome.cast.DefaultActionPolicy.CREATE_SESSION;
/** @constructor */
chrome.cast.Error = function() {};
/** @constructor */
chrome.cast.ReceiverAvailability = function() {};
/** @type {chrome.cast.ReceiverAvailability} */
chrome.cast.ReceiverAvailability.AVAILABLE;
/** @type {chrome.cast.ReceiverAvailability} */
chrome.cast.ReceiverAvailability.UNAVAILABLE;
/** @type {Object} */
chrome.cast.media = {};
/** @constructor */
chrome.cast.media.Media = function() {
/** @type {number} */
this.mediaSessionId = 0;
};
/** @constructor */
chrome.cast.Session = function() {
/** @type {Array.<chrome.cast.media.Media>} */
this.media = [];
/** @type {string} */
this.sessionId = '';
};
/**
* @param {string} namespace
* @param {Object} message
* @param {function():void} successCallback
* @param {function(chrome.cast.Error):void} errorCallback
*/
chrome.cast.Session.prototype.sendMessage =
function(namespace, message, successCallback, errorCallback) {};
/**
* @param {function(chrome.cast.media.Media):void} listener
*/
chrome.cast.Session.prototype.addMediaListener = function(listener) {};
/**
* @param {function(boolean):void} listener
*/
chrome.cast.Session.prototype.addUpdateListener = function(listener) {};
/**
* @param {string} namespace
* @param {function(chrome.cast.media.Media):void} listener
*/
chrome.cast.Session.prototype.addMessageListener =
function(namespace, listener){};
/**
* @param {function():void} successCallback
* @param {function(chrome.cast.Error):void} errorCallback
*/
chrome.cast.Session.prototype.stop =
function(successCallback, errorCallback) {};
/**
* @constructor
* @param {string} applicationID
*/
chrome.cast.SessionRequest = function(applicationID) {};
/**
* @constructor
* @param {chrome.cast.SessionRequest} sessionRequest
* @param {function(chrome.cast.Session):void} sessionListener
* @param {function(chrome.cast.ReceiverAvailability):void} receiverListener
* @param {chrome.cast.AutoJoinPolicy=} opt_autoJoinPolicy
* @param {chrome.cast.DefaultActionPolicy=} opt_defaultActionPolicy
*/
chrome.cast.ApiConfig = function(sessionRequest,
sessionListener,
receiverListener,
opt_autoJoinPolicy,
opt_defaultActionPolicy) {};
/**
* @param {chrome.cast.ApiConfig} apiConfig
* @param {function():void} onInitSuccess
* @param {function(chrome.cast.Error):void} onInitError
*/
chrome.cast.initialize =
function(apiConfig, onInitSuccess, onInitError) {};
/**
* @param {function(chrome.cast.Session):void} successCallback
* @param {function(chrome.cast.Error):void} errorCallback
*/
chrome.cast.requestSession =
function(successCallback, errorCallback) {};
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
"js": [ "cs_third_party_auth_trampoline.js" ] "js": [ "cs_third_party_auth_trampoline.js" ]
} }
], ],
"content_security_policy": "default-src 'self'; script-src 'self' {{ TALK_GADGET_HOST }}; style-src 'self' https://fonts.googleapis.com; img-src 'self' {{ TALK_GADGET_HOST }} data:; font-src *; connect-src 'self' {{ OAUTH2_ACCOUNTS_HOST }} {{ GOOGLE_API_HOSTS }} {{ TALK_GADGET_HOST }} https://relay.google.com", "content_security_policy": "default-src 'self'; script-src 'self' {{ TALK_GADGET_HOST }} https://www.gstatic.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' {{ TALK_GADGET_HOST }} data:; font-src *; connect-src 'self' {{ OAUTH2_ACCOUNTS_HOST }} {{ GOOGLE_API_HOSTS }} {{ TALK_GADGET_HOST }} https://relay.google.com",
{% endif %} {% endif %}
"optional_permissions": [ "optional_permissions": [
"<all_urls>" "<all_urls>"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment