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; |
+}); |
+ |