1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 define('media_router_bindings', [
8 'mojo/public/js/bindings',
10 'content/public/renderer/service_provider',
11 'chrome/browser/media/router/media_router.mojom',
12 'extensions/common/mojo/keep_alive.mojom',
13 'mojo/public/js/connection',
14 'mojo/public/js/router',
25 * Converts a media sink to a MediaSink Mojo object.
26 * @param {!MediaSink} sink A media sink.
27 * @return {!mediaRouterMojom.MediaSink} A Mojo MediaSink object.
29 function sinkToMojo_(sink
) {
30 return new mediaRouterMojom
.MediaSink({
31 'name': sink
.friendlyName
,
33 'is_launching': sink
.isLaunching_
,
38 * Returns a Mojo MediaRoute object given a MediaRoute and a
40 * @param {!MediaRoute} route
41 * @param {!string} sinkName
42 * @return {!mojo.MediaRoute}
44 function routeToMojo_(route
, sinkName
) {
45 return new mediaRouterMojom
.MediaRoute({
46 'media_route_id': route
.id
,
47 'media_source': route
.mediaSource
,
48 'media_sink': new mediaRouterMojom
.MediaSink({
49 'sink_id': route
.sinkId
,
52 'description': route
.description
,
53 'icon_url': route
.iconUrl
,
54 'is_local': route
.isLocal
,
55 'custom_controller_path': route
.customControllerPath
,
60 * Converts a route message to a RouteMessage Mojo object.
61 * @param {!RouteMessage} message
62 * @return {!mediaRouterMojom.RouteMessage} A Mojo RouteMessage object.
64 function messageToMojo_(message
) {
65 if ("string" == typeof message
.message
) {
66 return new mediaRouterMojom
.RouteMessage({
67 'type': mediaRouterMojom
.RouteMessage
.Type
.TEXT
,
68 'message': message
.message
,
71 return new mediaRouterMojom
.RouteMessage({
72 'type': mediaRouterMojom
.RouteMessage
.Type
.BINARY
,
73 'data': message
.message
,
79 * Creates a new MediaRouter.
80 * Converts a route struct to its Mojo form.
81 * @param {!MediaRouterService} service
84 function MediaRouter(service
) {
86 * The Mojo service proxy. Allows extension code to call methods that reside
88 * @type {!MediaRouterService}
90 this.service_
= service
;
93 * The provider manager service delegate. Its methods are called by the
94 * browser-resident Mojo service.
95 * @type {!MediaRouter}
97 this.mrpm_
= new MediaRouteProvider(this);
100 * The message pipe that connects the Media Router to mrpm_ across
101 * browser/renderer IPC boundaries. Object must remain in scope for the
102 * lifetime of the connection to prevent the connection from closing
104 * @type {!mojo.MessagePipe}
106 this.pipe_
= core
.createMessagePipe();
109 * Handle to a KeepAlive service object, which prevents the extension from
110 * being suspended as long as it remains in scope.
113 this.keepAlive_
= null;
116 * The stub used to bind the service delegate to the Mojo interface.
117 * Object must remain in scope for the lifetime of the connection to
118 * prevent the connection from closing automatically.
119 * @type {!mojom.MediaRouter}
121 this.mediaRouteProviderStub_
= connector
.bindHandleToStub(
122 this.pipe_
.handle0
, mediaRouterMojom
.MediaRouteProvider
);
124 // Link mediaRouteProviderStub_ to the provider manager delegate.
125 bindings
.StubBindings(this.mediaRouteProviderStub_
).delegate
= this.mrpm_
;
129 * Registers the Media Router Provider Manager with the Media Router.
130 * @return {!Promise<string>} Instance ID for the Media Router.
132 MediaRouter
.prototype.start = function() {
133 return this.service_
.registerMediaRouteProvider(this.pipe_
.handle1
).then(
135 return result
.instance_id
;
140 * Sets the service delegate methods.
141 * @param {Object} handlers
143 MediaRouter
.prototype.setHandlers = function(handlers
) {
144 this.mrpm_
.setHandlers(handlers
);
148 * The keep alive status.
151 MediaRouter
.prototype.getKeepAlive = function() {
152 return this.keepAlive_
!= null;
156 * Called by the provider manager when a sink list for a given source is
158 * @param {!string} sourceUrn
159 * @param {!Array<!MediaSink>} sinks
161 MediaRouter
.prototype.onSinksReceived = function(sourceUrn
, sinks
) {
162 this.service_
.onSinksReceived(sourceUrn
, sinks
.map(sinkToMojo_
));
166 * Called by the provider manager to keep the extension from suspending
167 * if it enters a state where suspension is undesirable (e.g. there is an
168 * active MediaRoute.)
169 * If keepAlive is true, the extension is kept alive.
170 * If keepAlive is false, the extension is allowed to suspend.
171 * @param {boolean} keepAlive
173 MediaRouter
.prototype.setKeepAlive = function(keepAlive
) {
174 if (keepAlive
=== false && this.keepAlive_
) {
175 this.keepAlive_
.close();
176 this.keepAlive_
= null;
177 } else if (keepAlive
=== true && !this.keepAlive_
) {
178 this.keepAlive_
= new routerModule
.Router(
179 serviceProvider
.connectToService(
180 keepAliveMojom
.KeepAlive
.name
));
185 * Called by the provider manager to send an issue from a media route
186 * provider to the Media Router, to show the user.
187 * @param {!Object} issue The issue object.
189 MediaRouter
.prototype.onIssue = function(issue
) {
190 function issueSeverityToMojo_(severity
) {
193 return mediaRouterMojom
.Issue
.Severity
.FATAL
;
195 return mediaRouterMojom
.Issue
.Severity
.WARNING
;
197 return mediaRouterMojom
.Issue
.Severity
.NOTIFICATION
;
199 console
.error('Unknown issue severity: ' + severity
);
200 return mediaRouterMojom
.Issue
.Severity
.NOTIFICATION
;
204 function issueActionToMojo_(action
) {
207 return mediaRouterMojom
.Issue
.ActionType
.OK
;
209 return mediaRouterMojom
.Issue
.ActionType
.CANCEL
;
211 return mediaRouterMojom
.Issue
.ActionType
.DISMISS
;
213 return mediaRouterMojom
.Issue
.ActionType
.LEARN_MORE
;
215 console
.error('Unknown issue action type : ' + action
);
216 return mediaRouterMojom
.Issue
.ActionType
.OK
;
220 var secondaryActions
= (issue
.secondaryActions
|| []).map(function(e
) {
221 return issueActionToMojo_(e
);
223 this.service_
.onIssue(new mediaRouterMojom
.Issue({
224 'route_id': issue
.routeId
,
225 'severity': issueSeverityToMojo_(issue
.severity
),
226 'title': issue
.title
,
227 'message': issue
.message
,
228 'default_action': issueActionToMojo_(issue
.defaultAction
),
229 'secondary_actions': secondaryActions
,
230 'help_url': issue
.helpUrl
,
231 'is_blocking': issue
.isBlocking
236 * Called by the provider manager when the set of active routes
238 * @param {!Array<MediaRoute>} routes The active set of media routes.
239 * @param {!Array<MediaSink>} sinks The active set of media sinks.
241 MediaRouter
.prototype.onRoutesUpdated = function(routes
, sinks
) {
242 // Create an inverted index relating sink IDs to their names.
243 var sinkNameMap
= {};
244 for (var i
= 0; i
< sinks
.length
; i
++) {
245 sinkNameMap
[sinks
[i
].id
] = sinks
[i
].friendlyName
;
248 // Convert MediaRoutes to Mojo objects and add their sink names
250 var mojoRoutes
= routes
.map(function(nextRoute
) {
251 return routeToMojo_(nextRoute
, sinkNameMap
[nextRoute
.sinkId
]);
254 this.service_
.onRoutesUpdated(mojoRoutes
, sinks
.map(sinkToMojo_
));
258 * Called by the Provider Manager when an error was encountered in response
259 * to a media route creation request.
260 * @param {!string} requestId The request id.
261 * @param {!string} error The error.
263 MediaRouter
.prototype.onRouteResponseError
=
264 function(requestId
, error
) {
265 this.service_
.onRouteResponseError(requestId
, error
);
269 * Called by the provider manager when a route was able to be created by a
270 * media route provider.
272 * @param {string} requestId The media route request id.
273 * @param {string} routeId The id of the media route that was created.
275 MediaRouter
.prototype.onRouteResponseReceived
=
276 function(requestId
, routeId
) {
277 this.service_
.onRouteResponseReceived(requestId
, routeId
);
281 * Object containing callbacks set by the provider manager.
282 * TODO(mfoltz): Better named ProviderManagerDelegate?
287 function MediaRouterHandlers() {
289 * @type {function(!string, !string, !string, !string, !number}
291 this.createRoute
= null;
294 * @type {function(!string, !string, !string, !number)}
296 this.joinRoute
= null;
299 * @type {function(string)}
301 this.closeRoute
= null;
304 * @type {function(string)}
306 this.startObservingMediaSinks
= null;
309 * @type {function(string)}
311 this.stopObservingMediaSinks
= null;
314 * @type {function(string, string): Promise}
316 this.sendRouteMessage
= null;
319 * @type {function(string, Uint8Array): Promise}
321 this.sendRouteBinaryMessage
= null;
324 * @type {function(string): Promise.<Array.<RouteMessage>>}
326 this.listenForRouteMessages
= null;
331 this.startObservingMediaRoutes
= null;
336 this.stopObservingMediaRoutes
= null;
340 * Routes calls from Media Router to the provider manager extension.
341 * Registered with the MediaRouter stub.
342 * @param {!MediaRouter} MediaRouter proxy to call into the
343 * Media Router mojo interface.
346 function MediaRouteProvider(mediaRouter
) {
347 mediaRouterMojom
.MediaRouteProvider
.stubClass
.call(this);
350 * Object containing JS callbacks into Provider Manager code.
351 * @type {!MediaRouterHandlers}
353 this.handlers_
= new MediaRouterHandlers();
356 * Proxy class to the browser's Media Router Mojo service.
357 * @type {!MediaRouter}
359 this.mediaRouter_
= mediaRouter
;
361 MediaRouteProvider
.prototype = Object
.create(
362 mediaRouterMojom
.MediaRouteProvider
.stubClass
.prototype);
365 * Sets the callback handler used to invoke methods in the provider manager.
367 * TODO(mfoltz): Rename to something more explicit?
368 * @param {!MediaRouterHandlers} handlers
370 MediaRouteProvider
.prototype.setHandlers = function(handlers
) {
371 this.handlers_
= handlers
;
372 var requiredHandlers
= [
373 'stopObservingMediaRoutes',
374 'startObservingMediaRoutes',
376 'sendRouteBinaryMessage',
377 'listenForRouteMessages',
381 'stopObservingMediaSinks',
382 'startObservingMediaRoutes'
384 requiredHandlers
.forEach(function(nextHandler
) {
385 if (handlers
[nextHandler
] === undefined) {
386 console
.error(nextHandler
+ ' handler not registered.');
392 * Starts querying for sinks capable of displaying the media source
393 * designated by |sourceUrn|. Results are returned by calling
395 * @param {!string} sourceUrn
397 MediaRouteProvider
.prototype.startObservingMediaSinks
=
398 function(sourceUrn
) {
399 this.handlers_
.startObservingMediaSinks(sourceUrn
);
403 * Stops querying for sinks capable of displaying |sourceUrn|.
404 * @param {!string} sourceUrn
406 MediaRouteProvider
.prototype.stopObservingMediaSinks
=
407 function(sourceUrn
) {
408 this.handlers_
.stopObservingMediaSinks(sourceUrn
);
412 * Requests that |sinkId| render the media referenced by |sourceUrn|. If the
413 * request is from the Presentation API, then origin and tabId will
415 * @param {!string} sourceUrn Media source to render.
416 * @param {!string} sinkId Media sink ID.
417 * @param {!string} presentationId Presentation ID from the site
418 * requesting presentation. TODO(mfoltz): Remove.
419 * @param {!string} origin Origin of site requesting presentation.
420 * @param {!number} tabId ID of tab requesting presentation.
421 * @return {!Promise.<!Object>} A Promise resolving to an object describing
422 * the newly created media route, or rejecting with an error message on
425 MediaRouteProvider
.prototype.createRoute
=
426 function(sourceUrn
, sinkId
, presentationId
, origin
, tabId
) {
427 return this.handlers_
.createRoute(
428 sourceUrn
, sinkId
, presentationId
, origin
, tabId
)
429 .then(function(route
) {
430 // Sink name is not used, so it is omitted here.
431 return {route
: routeToMojo_(route
, "")};
433 .catch(function(err
) {
434 return {error_text
: 'Error creating route: ' + err
.message
};
439 * Handles a request via the Presentation API to join an existing route given
440 * by |sourceUrn| and |presentationId|. |origin| and |tabId| are used for
441 * validating same-origin/tab scope.
442 * @param {!string} sourceUrn Media source to render.
443 * @param {!string} presentationId Presentation ID to join.
444 * @param {!string} origin Origin of site requesting join.
445 * @param {!number} tabId ID of tab requesting join.
446 * @return {!Promise.<!Object>} A Promise resolving to an object describing
447 * the newly created media route, or rejecting with an error message on
450 MediaRouteProvider
.prototype.joinRoute
=
451 function(sourceUrn
, presentationId
, origin
, tabId
) {
452 return this.handlers_
.joinRoute(sourceUrn
, presentationId
, origin
, tabId
)
453 .then(function(newRoute
) {
454 // Sink name is not used, so it is omitted here.
455 return {route
: routeToMojo_(newRoute
, "")};
458 return {error_text
: 'Error joining route: ' + err
.message
};
463 * Closes the route specified by |routeId|.
464 * @param {!string} routeId
466 MediaRouteProvider
.prototype.closeRoute = function(routeId
) {
467 this.handlers_
.closeRoute(routeId
);
471 * Posts a message to the route designated by |routeId|.
472 * @param {!string} routeId
473 * @param {!string} message
474 * @return {!Promise.<boolean>} Resolved with true if the message was sent,
475 * or false on failure.
477 MediaRouteProvider
.prototype.sendRouteMessage = function(
479 return this.handlers_
.sendRouteMessage(routeId
, message
)
481 return {'sent': true};
483 return {'sent': false};
488 * Sends a binary message to the route designated by |routeId|.
489 * @param {!string} routeId
490 * @param {!Uint8Array} data
491 * @return {!Promise.<boolean>} Resolved with true if the data was sent,
492 * or false on failure.
494 MediaRouteProvider
.prototype.sendRouteBinaryMessage = function(
496 return this.handlers_
.sendRouteBinaryMessage(routeId
, data
)
498 return {'sent': true};
500 return {'sent': false};
505 * Listen for next batch of messages from one of the routeIds.
506 * @param {!string} routeId
507 * @return {!Promise.<Array.<RouteMessage>>} Resolved with a list of messages,
508 * an empty list if an error occurred.
510 MediaRouteProvider
.prototype.listenForRouteMessages = function(routeId
) {
511 return this.handlers_
.listenForRouteMessages([routeId
])
512 .then(function(messages
) {
513 return {'messages': messages
.map(messageToMojo_
)};
515 return {'messages': []};
520 * Requests that the provider manager start sending information about active
521 * media routes to the Media Router.
523 MediaRouteProvider
.prototype.startObservingMediaRoutes = function() {
524 this.handlers_
.startObservingMediaRoutes();
528 * Requests that the provider manager stop sending information about active
529 * media routes to the Media Router.
531 MediaRouteProvider
.prototype.stopObservingMediaRoutes = function() {
532 this.handlers_
.stopObservingMediaRoutes();
535 mediaRouter
= new MediaRouter(connector
.bindHandleToProxy(
536 serviceProvider
.connectToService(
537 mediaRouterMojom
.MediaRouter
.name
),
538 mediaRouterMojom
.MediaRouter
));