Chromium Code Reviews| Index: extensions/renderer/resources/media_router_bindings.js |
| diff --git a/extensions/renderer/resources/media_router_bindings.js b/extensions/renderer/resources/media_router_bindings.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8cbf0b0c0c83ad33762efad6cfa66095ea7a9419 |
| --- /dev/null |
| +++ b/extensions/renderer/resources/media_router_bindings.js |
| @@ -0,0 +1,503 @@ |
| +// 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. |
| + |
| +var mediaRouterObserver; |
| + |
| +define('media_router_bindings', [ |
| + 'mojo/public/js/bindings', |
| + 'mojo/public/js/core', |
| + 'content/public/renderer/service_provider', |
| + 'chrome/browser/media/router/media_router.mojom', |
| + 'extensions/common/mojo/keep_alive.mojom', |
| + 'mojo/public/js/connection', |
| + 'mojo/public/js/router', |
| +], function(bindings, |
| + core, |
| + serviceProvider, |
| + mediaRouterMojom, |
| + keepAliveMojom, |
| + connector, |
| + routerModule) { |
| + 'use strict'; |
| + |
| + |
| + /** |
| + * Converts a sink object to a MediaSink Mojo object. |
| + * @param {!Object} sink |
| + * @return {!mediaRouterMojom.MediaSink} |
| + */ |
| + function sinkToMojo_(function(sink) { |
| + return new mediaRouterMojom.MediaSink({ |
| + 'name': sink.friendlyName, |
| + 'sink_id': sink.id, |
| + }); |
| + } |
| + |
| + |
| + /** |
| + * Converts a route struct to its Mojo form. |
| + * |
| + * @param {!MediaRoute} route |
| + * @param {!string=} opt_sinkName |
| + * @return {!mojo.MediaRoute} |
| + */ |
| + function routeToMojo_(route, opt_sinkName) { |
| + return new mediaRouterMojom.MediaRoute({ |
| + 'media_route_id': route.id, |
| + 'media_source': route.mediaSource, |
| + 'media_sink': new mediaRouterMojom.MediaSink({ |
| + 'sink_id': route.sinkId, |
| + 'name': opt_sinkName, |
| + }), |
| + 'description': route.description, |
| + 'icon_url': route.iconUrl, |
| + 'is_local': route.isLocal |
| + }); |
| + } |
| + |
| + /** |
| + * @param {!MediaRouterService} service |
| + * @constructor |
| + */ |
| + function MediaRouterObserver(service) { |
| + /** |
| + * The Mojo service proxy. Allows extension code to call methods that reside |
| + * in the browser. |
| + * @type {!MediaRouterService} |
| + */ |
| + this.observer_ = service; |
|
Devlin
2015/06/04 23:01:19
Wait, why does a MediaRouterObserver have an obser
Kevin M
2015/06/08 17:32:59
Done.
|
| + |
| + /** |
| + * The MRPM service delegate. Its methods are called by the browser-resident |
| + * Mojo service. |
| + * @type {!MediaRouter} |
| + */ |
| + this.mrpm_ = new MediaRouter(this); |
|
Devlin
2015/06/04 23:01:19
Also quite odd for the MediaRouterObserver to own
Kevin M
2015/06/08 17:32:59
Agreed with the awkward names. We'll do a quality
|
| + |
| + /** |
| + * The message pipe that connects the Media Router to mrpm_ across |
| + * browser/renderer IPC boundaries. |
| + * Object must remain in scope for the lifetime of the connection to |
| + * prevent the connection from closing automatically. |
| + * @type {!mojo.MessagePipe} |
| + */ |
| + this.pipe_ = core.createMessagePipe(); |
| + |
| + /** |
| + * Handle to a KeepAlive service object, which prevents the extension from |
| + * being suspended as long as it remains in scope |
| + * @type {boolean} |
| + */ |
| + this.keepAlive_ = null; |
| + |
| + // Define the stub used to bind this.mrpm_ to the Mojo interface. |
|
Devlin
2015/06/04 22:42:25
jsdoc-style comments
|
| + // Object must remain in scope for the lifetime of the connection to |
| + // prevent the connection from closing automatically. |
| + // @type {!mojom.MediaRouter} |
| + this.mediaRouterStub_ = connector.bindHandleToStub( |
| + this.pipe_.handle0, mediaRouterMojom.MediaRouter); |
| + |
| + // Link the stub to impl code. |
| + bindings.StubBindings(this.mediaRouterStub_).delegate = this.mrpm_; |
| + } |
| + |
| + |
| + /** |
| + * Register the Media Router Provider Manager with the Media Router. |
|
Devlin
2015/06/04 22:42:25
nit: function comments should be descriptive, i.e.
Kevin M
2015/06/08 17:32:59
Thanks, done.
|
| + * @return {!Promise<string>} A unique, string-based ID for this instance |
| + * of the Media Router. |
| + */ |
| + MediaRouterObserver.prototype.start = function() { |
| + return this.observer_.provideMediaRouter(this.pipe_.handle1).then( |
| + function(result) { |
| + return result.instance_id; |
| + }.bind(this)); |
| + } |
| + |
| + |
| + /** |
| + * Sets the service delegate methods. |
| + * @param {Object} |
|
Devlin
2015/06/04 22:42:26
handlers
Kevin M
2015/06/08 17:32:59
Done.
|
| + */ |
| + MediaRouterObserver.prototype.setHandlers = function(handlers) { |
| + this.mrpm_.setHandlers(handlers); |
| + } |
| + |
| + |
| + /** |
| + * Gets the keep alive status. |
| + * @return {boolean} |
| + */ |
| + MediaRouterObserver.prototype.getKeepAlive = function() { |
| + return this.keepAlive_ != null; |
| + }; |
| + |
| + |
| + /** |
| + * Called by the Provider Manager when a sink list for a given source is |
| + * updated. |
| + * @param {!string} sourceUrn |
| + * @param {!Array<!Object>} sinks |
| + */ |
| + MediaRouterObserver.prototype.onSinksReceived = function(sourceUrn, sinks) { |
| + this.observer_.onSinksReceived(sourceUrn, |
| + sinks.map(sinkToMojo_)); |
| + }; |
| + |
| + |
| + /** |
| + * Called by the Provider Manager to keep the extension from suspending |
| + * if it enters a state where suspension is undesirable (e.g. there is an |
| + * active MediaRoute.) |
| + * If keepAlive is true, the extension is kept alive. |
| + * If keepAlive is false, the extension is allowed to suspend. |
| + * |
| + * @param {boolean} keepAlive |
| + */ |
| + MediaRouterObserver.prototype.setKeepAlive = function(keepAlive) { |
| + if (keepAlive === false && this.keepAlive_) { |
| + this.keepAlive_.close(); |
| + this.keepAlive_ = null; |
| + } else if (keepAlive === true && !this.keepAlive_) { |
| + this.keepAlive_ = new routerModule.Router( |
| + serviceProvider.connectToService( |
| + keepAliveMojom.KeepAlive.name)); |
| + } |
| + }; |
| + |
|
Devlin
2015/06/04 22:42:25
I don't think we have the double-newline style.
Kevin M
2015/06/08 17:32:59
Done.
|
| + |
| + /** |
| + * Sends a message to an active media route. |
| + * |
|
Devlin
2015/06/04 23:01:19
remove (and below)
Kevin M
2015/06/08 17:32:58
Done.
|
| + * @param {!string} routeId |
| + * @param {!Object|string} message A message that can be converted to a JSON |
| + * string. |
| + */ |
| + MediaRouterObserver.prototype.onMessage = function(routeId, message) { |
| + this.observer_.onMessage(routeId, JSON.stringify(message)); |
| + }; |
| + |
| + |
| + /** |
| + * Reports an issue to the Media Router. |
| + * |
| + * @param {!Object} issue |
| + */ |
| + MediaRouterObserver.prototype.onIssue = function(issue) { |
| + function issueSeverityToMojo_(severity) { |
| + switch (severity) { |
| + case 'fatal': |
| + return mediaRouterMojom.Issue.Severity.FATAL; |
| + case 'warning': |
| + return mediaRouterMojom.Issue.Severity.WARNING; |
| + case 'notification': |
| + return mediaRouterMojom.Issue.Severity.NOTIFICATION; |
| + default: |
| + console.error('Unknown issue severity: ' + severity); |
| + return mediaRouterMojom.Issue.Severity.NOTIFICATION; |
| + } |
| + } |
| + |
| + function issueActionToMojo_(action) { |
| + switch (action) { |
| + case 'ok': |
| + return mediaRouterMojom.Issue.ActionType.OK; |
| + case 'cancel': |
| + return mediaRouterMojom.Issue.ActionType.CANCEL; |
| + case 'dismiss': |
| + return mediaRouterMojom.Issue.ActionType.DISMISS; |
| + case 'learn_more': |
| + return mediaRouterMojom.Issue.ActionType.LEARN_MORE; |
| + default: |
| + console.error('Unknown issue action type : ' + action); |
| + return mediaRouterMojom.Issue.ActionType.OK; |
| + } |
| + } |
| + |
| + var secondaryActions = (issue.secondaryActions || []).map(function(e) { |
| + return issueActionToMojo_(e); |
| + }); |
| + this.observer_.onIssue(new mediaRouterMojom.Issue({ |
| + 'route_id': issue.routeId, |
| + 'severity': issueSeverityToMojo_(issue.severity), |
| + 'title': issue.title, |
| + 'message': issue.message, |
| + 'default_action': issueActionToMojo_(issue.defaultAction), |
| + 'secondary_actions': secondaryActions, |
| + 'help_url': issue.helpUrl, |
| + 'is_blocking': issue.isBlocking |
| + })); |
| + }; |
| + |
| + |
| + /** |
| + * Called by the Provider Manager when the list of active routes |
| + * has changed. |
| + * @param {!Array<MediaRoute>} routes |
| + * @param {!Array<MediaSink>} routes |
|
Devlin
2015/06/04 23:01:19
sinks?
Kevin M
2015/06/08 17:32:58
Done.
|
| + */ |
| + MediaRouterObserver.prototype.onRoutesUpdated = function(routes, sinks) { |
| + // Create an inverted index relating sink IDs to their names. |
| + var sinkNameMap = {}; |
| + for (var i = 0; i < sinks.length; i++) { |
|
Devlin
2015/06/04 23:01:19
nit: forEach() is surprisingly faster than for (va
Kevin M
2015/06/08 17:32:58
Is this still the case? I ran this benchmark suite
Devlin
2015/06/08 19:51:41
Huh. Used to be the inverse. Also, it's importan
|
| + sinkNameMap[sinks[i].id] = sinks[i].friendlyName; |
| + } |
| + |
| + // Convert MediaRoutes to Mojo objects and add their sink names |
| + // via sinkNameMap. |
| + var mojoRoutes = []; |
| + for (var j = 0; j < routes.length; j++) { |
| + mojoRoutes.push(routeToMojo_(routes[j], sinkNameMap[routes[j].sinkId])); |
| + } |
| + |
| + this.observer_.onRoutesUpdated( |
| + mojoRoutes, |
| + sinks.map(MediaRouterObserver.sinkToMojo_)); |
| + }; |
| + |
| + |
| + /** |
| + * Called by the Provider Manager when an error was encountered for a media |
| + * route. |
| + * |
| + * @param {!string} requestId The ID of the route request which experienced an |
| + * error. |
| + * @param {!string} error |
| + */ |
| + MediaRouterObserver.prototype.onRouteResponseError = |
| + function(requestId, error) { |
| + this.observer_.onRouteResponseError(requestId, error); |
| + }; |
| + |
| + |
| + MediaRouterObserver.prototype.onRouteResponseReceived = |
| + function(requestId, routeId) { |
| + this.observer_.onRouteResponseReceived(requestId, routeId); |
| + }; |
| + |
| + |
| + /** |
| + * Object containing JS callbacks into Provider Manager code. |
| + * @constructor |
| + * @struct |
| + */ |
| + function MediaRouterHandlers() { |
| + /** |
| + * @type {function(!string, !string, !string=, !string=, !number=} |
| + */ |
| + this.createRoute = null; |
| + |
| + /** |
| + * @type {function(!string, !string, !string, !number)} |
| + */ |
| + this.joinRoute = null; |
| + |
| + /** |
| + * @type {function(string)} |
| + */ |
| + this.closeRoute = null; |
| + |
| + /** |
| + * @type {function(string)} |
| + */ |
| + this.startObservingMediaSinks = null; |
| + |
| + /** |
| + * @type {function(string)} |
| + */ |
| + this.stopObservingMediaSinks = null; |
| + |
| + /** |
| + * @type {function(string, string, string)} |
| + */ |
| + this.postMessage = null; |
| + |
| + /** |
| + * @type {function()} |
| + */ |
| + this.startObservingMediaRoutes = null; |
| + |
| + /** |
| + * @type {function()} |
| + */ |
| + this.stopObservingMediaRoutes = null; |
| + }; |
| + |
| + |
| + /** |
| + * Routes calls from Media Router to the Provider Manager extension. |
| + * Registered with the MediaRouter stub. |
| + * |
| + * @constructor |
| + */ |
| + function MediaRouter(mediaRouterObserver) { |
| + /** |
| + * Object containing JS callbacks into Provider Manager code. |
| + * @type {!MediaRouterHandlers} |
| + */ |
| + this.handlers_ = new MediaRouterHandlers(); |
| + |
| + /** |
| + * Proxy class to the browser's Media Router Mojo service. |
| + * @type {!MediaRouterObserver} |
| + */ |
| + this.mediaRouter_ = mediaRouterObserver; |
|
Devlin
2015/06/04 23:01:19
Why is |mediaRouter_| a mediaRouterObserver? And
Kevin M
2015/06/08 17:32:58
A previous code reviewer thought it strange to hav
|
| + } |
| + MediaRouter.prototype = Object.create( |
| + mediaRouterMojom.MediaRouter.stubClass.prototype); |
| + |
| + |
| + /* |
| + * Sets the callback handler used to invoke methods in the Provider Manager. |
| + * @param {!MediaRouterHandlers} handlers |
| + */ |
| + MediaRouter.prototype.setHandlers = function(handlers) { |
| + this.handlers_ = handlers; |
| + if (!this.handlers_.stopObservingMediaRoutes) { |
|
Devlin
2015/06/04 23:01:19
var requiredHandlers = ['stopObservingMediaRouters
Kevin M
2015/06/08 17:32:59
Done.
|
| + console.error('stopObservingMediaRoutes handler not registered.'); |
| + return; |
| + } |
| + if (!this.handlers_.startObservingMediaRoutes) { |
| + console.error('startObservingMediaRoutes handler not registered.'); |
| + return; |
| + } |
| + if (!this.handlers_.postMessage) { |
| + console.error('postMessage handler not registered.'); |
| + return; |
| + } |
| + if (!this.handlers_.closeRoute) { |
| + console.error('closeRoute handler not registered.'); |
| + return; |
| + } |
| + if (!this.handlers_.joinRoute) { |
| + console.error('joinRoute handler not registered.'); |
| + return; |
| + } |
| + if (!this.handlers_.createRoute) { |
| + console.error('createRoute handler not registered.'); |
| + return; |
| + } |
| + if (!this.handlers_.stopObservingMediaSinks) { |
| + console.error('stopObservingMediaSinks handler not registered.'); |
| + return; |
| + } |
| + if ( !this.handlers_.startObservingMediaSinks) { |
| + console.error('startObservingMediaSinks handler not registered.'); |
| + return; |
| + } |
| + } |
| + |
| + |
| + /** |
| + * Starts querying for sinks capable of displaying the media |sourceUrn|. |
| + * Results are returned by calling OnSinksReceived. |
| + * @param {!string} sourceUrn |
| + */ |
| + MediaRouter.prototype.startObservingMediaSinks = |
| + function(sourceUrn) { |
| + this.handlers_.startObservingMediaSinks(sourceUrn); |
| + }; |
| + |
| + |
| + /** |
| + * Stops querying for sinks capable of displaying the media |sourceUrn|. |
| + * @param {!string} sourceUrn |
| + */ |
| + MediaRouter.prototype.stopObservingMediaSinks = |
| + function(sourceUrn) { |
| + this.handlers_.stopObservingMediaSinks(sourceUrn); |
| + }; |
| + |
| + |
| + /** |
| + * Requests that |sinkId| render the media referenced by |sourceUrn|. |
| + * @param {!string} sourceUrn |
| + * @param {!string} sinkId |
| + * @param {!string=} opt_presentationId |
| + * @param {!string=} opt_origin |
| + * @param {!number=} opt_tabId |
| + * @return {!Promise.<!Object>} |
|
Devlin
2015/06/04 23:01:19
Kind of a shame that this can't be a Promise<mojo.
Kevin M
2015/06/08 17:32:59
I agree, but Mojo's JS binding library doesn't hav
Devlin
2015/06/08 19:51:41
Oh, bummer.
|
| + */ |
| + MediaRouter.prototype.createRoute = |
| + function(sourceUrn, sinkId, opt_presentationId, opt_origin, opt_tabId) { |
| + return this.handlers_.createRoute( |
| + sourceUrn, sinkId, opt_presentationId, opt_origin, opt_tabId) |
| + .then(function(route) { |
| + // Sink name is not used, so it is omitted here. |
| + return {route: routeToMojo_(route, "")}; |
| + }.bind(this)) |
| + .catch(function(err) { |
| + return {error_text: err.message}; |
| + }); |
| + }; |
| + |
| + /** |
| + * Join an existing route given by |presentationId| and render media |
| + * referenced by |sourceUrn|. |origin| and |tabId| are used for checking |
| + * origin/tab scope. |
| + * @param {!string} sourceUrn |
| + * @param {!string} presentationId |
| + * @param {!string} origin |
| + * @param {!number} tabId |
| + * @return {!Promise.<!Object>} Resolved with the route on success, |
| + * or with an error message on failure. |
| + */ |
| + MediaRouter.prototype.joinRoute = |
| + function(sourceUrn, presentationId, origin, tabId) { |
| + return this.handlers_.joinRoute(sourceUrn, presentationId, origin, tabId) |
| + .then(function(newRoute) { |
| + return {route: routeToMojo_(newRoute)}; |
| + }, |
| + function(err) { |
| + return {error_text: 'Error joining route: ' + err.message}; |
| + }); |
| + }; |
| + |
| + |
| + /** |
| + * Closes route specified by |routeId| |
| + * @param {!string} routeId |
| + */ |
| + MediaRouter.prototype.closeRoute = function(routeId) { |
| + this.handlers_.closeRoute(routeId); |
| + }; |
| + |
| + |
| + /** |
| + * Posts message to a sink connected by route with |routeId|. |
| + * @param {!string} routeId |
| + * @param {!string} message |
| + * @param {string} extraInfoJson |
| + */ |
| + MediaRouter.prototype.postMessage = function( |
| + routeId, message, extraInfoJson) { |
| + this.handlers_.postMessage(routeId, message, JSON.parse(extraInfoJson)); |
| + }; |
| + |
| + |
| + /** |
| + * Requests that the Provider Manager start sending information about active |
| + * media routes to the Media Router. |
| + */ |
| + MediaRouter.prototype.startObservingMediaRoutes = function() { |
| + this.handlers_.startObservingMediaRoutes(); |
| + }; |
| + |
| + |
| + /** |
| + * Requests that the Provider Manager stop sending information about active |
| + * media routes to the Media Router. |
| + */ |
| + MediaRouter.prototype.stopObservingMediaRoutes = function() { |
| + this.handlers_.stopObservingMediaRoutes(); |
| + }; |
| + |
| + mediaRouterObserver = new MediaRouterObserver(connector.bindHandleToProxy( |
| + serviceProvider.connectToService( |
| + mediaRouterMojom.MediaRouterObserver.name), |
| + mediaRouterMojom.MediaRouterObserver)); |
| + |
| + return mediaRouterObserver; |
| +}); |
| + |