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 'icon_type': sinkIconTypeToMojo(sink
.iconType
),
34 'is_launching': sink
.isLaunching_
,
39 * Converts a media sink's icon type to a MediaSink.IconType Mojo object.
40 * @param {!MediaSink.IconType} type A media sink's icon type.
41 * @return {!mediaRouterMojom.MediaSink.IconType} A Mojo MediaSink.IconType
44 function sinkIconTypeToMojo(type
) {
47 return mediaRouterMojom
.MediaSink
.IconType
.CAST
;
49 return mediaRouterMojom
.MediaSink
.IconType
.CAST_AUDIO
;
51 return mediaRouterMojom
.MediaSink
.IconType
.GENERIC
;
53 return mediaRouterMojom
.MediaSink
.IconType
.HANGOUT
;
55 console
.error('Unknown sink icon type : ' + type
);
56 return mediaRouterMojom
.MediaSink
.IconType
.GENERIC
;
61 * Returns a Mojo MediaRoute object given a MediaRoute and a
63 * @param {!MediaRoute} route
64 * @return {!mojo.MediaRoute}
66 function routeToMojo_(route
) {
67 return new mediaRouterMojom
.MediaRoute({
68 'media_route_id': route
.id
,
69 'media_source': route
.mediaSource
,
70 'media_sink_id': route
.sinkId
,
71 'description': route
.description
,
72 'icon_url': route
.iconUrl
,
73 'is_local': route
.isLocal
,
74 'custom_controller_path': route
.customControllerPath
,
75 // TODO(imcheng): Remove logic when extension always sets the field.
76 'for_display': route
.forDisplay
== undefined ? true : route
.forDisplay
81 * Converts a route message to a RouteMessage Mojo object.
82 * @param {!RouteMessage} message
83 * @return {!mediaRouterMojom.RouteMessage} A Mojo RouteMessage object.
85 function messageToMojo_(message
) {
86 if ("string" == typeof message
.message
) {
87 return new mediaRouterMojom
.RouteMessage({
88 'type': mediaRouterMojom
.RouteMessage
.Type
.TEXT
,
89 'message': message
.message
,
92 return new mediaRouterMojom
.RouteMessage({
93 'type': mediaRouterMojom
.RouteMessage
.Type
.BINARY
,
94 'data': message
.message
,
100 * Creates a new MediaRouter.
101 * Converts a route struct to its Mojo form.
102 * @param {!MediaRouterService} service
105 function MediaRouter(service
) {
107 * The Mojo service proxy. Allows extension code to call methods that reside
109 * @type {!MediaRouterService}
111 this.service_
= service
;
114 * The provider manager service delegate. Its methods are called by the
115 * browser-resident Mojo service.
116 * @type {!MediaRouter}
118 this.mrpm_
= new MediaRouteProvider(this);
121 * The message pipe that connects the Media Router to mrpm_ across
122 * browser/renderer IPC boundaries. Object must remain in scope for the
123 * lifetime of the connection to prevent the connection from closing
125 * @type {!mojo.MessagePipe}
127 this.pipe_
= core
.createMessagePipe();
130 * Handle to a KeepAlive service object, which prevents the extension from
131 * being suspended as long as it remains in scope.
134 this.keepAlive_
= null;
137 * The stub used to bind the service delegate to the Mojo interface.
138 * Object must remain in scope for the lifetime of the connection to
139 * prevent the connection from closing automatically.
140 * @type {!mojom.MediaRouter}
142 this.mediaRouteProviderStub_
= connector
.bindHandleToStub(
143 this.pipe_
.handle0
, mediaRouterMojom
.MediaRouteProvider
);
145 // Link mediaRouteProviderStub_ to the provider manager delegate.
146 bindings
.StubBindings(this.mediaRouteProviderStub_
).delegate
= this.mrpm_
;
150 * Registers the Media Router Provider Manager with the Media Router.
151 * @return {!Promise<string>} Instance ID for the Media Router.
153 MediaRouter
.prototype.start = function() {
154 return this.service_
.registerMediaRouteProvider(this.pipe_
.handle1
).then(
156 return result
.instance_id
;
161 * Sets the service delegate methods.
162 * @param {Object} handlers
164 MediaRouter
.prototype.setHandlers = function(handlers
) {
165 this.mrpm_
.setHandlers(handlers
);
169 * The keep alive status.
172 MediaRouter
.prototype.getKeepAlive = function() {
173 return this.keepAlive_
!= null;
177 * Called by the provider manager when a sink list for a given source is
179 * @param {!string} sourceUrn
180 * @param {!Array<!MediaSink>} sinks
182 MediaRouter
.prototype.onSinksReceived = function(sourceUrn
, sinks
) {
183 this.service_
.onSinksReceived(sourceUrn
, sinks
.map(sinkToMojo_
));
187 * Called by the provider manager to keep the extension from suspending
188 * if it enters a state where suspension is undesirable (e.g. there is an
189 * active MediaRoute.)
190 * If keepAlive is true, the extension is kept alive.
191 * If keepAlive is false, the extension is allowed to suspend.
192 * @param {boolean} keepAlive
194 MediaRouter
.prototype.setKeepAlive = function(keepAlive
) {
195 if (keepAlive
=== false && this.keepAlive_
) {
196 this.keepAlive_
.close();
197 this.keepAlive_
= null;
198 } else if (keepAlive
=== true && !this.keepAlive_
) {
199 this.keepAlive_
= new routerModule
.Router(
200 serviceProvider
.connectToService(
201 keepAliveMojom
.KeepAlive
.name
));
206 * Called by the provider manager to send an issue from a media route
207 * provider to the Media Router, to show the user.
208 * @param {!Object} issue The issue object.
210 MediaRouter
.prototype.onIssue = function(issue
) {
211 function issueSeverityToMojo_(severity
) {
214 return mediaRouterMojom
.Issue
.Severity
.FATAL
;
216 return mediaRouterMojom
.Issue
.Severity
.WARNING
;
218 return mediaRouterMojom
.Issue
.Severity
.NOTIFICATION
;
220 console
.error('Unknown issue severity: ' + severity
);
221 return mediaRouterMojom
.Issue
.Severity
.NOTIFICATION
;
225 function issueActionToMojo_(action
) {
228 return mediaRouterMojom
.Issue
.ActionType
.OK
;
230 return mediaRouterMojom
.Issue
.ActionType
.CANCEL
;
232 return mediaRouterMojom
.Issue
.ActionType
.DISMISS
;
234 return mediaRouterMojom
.Issue
.ActionType
.LEARN_MORE
;
236 console
.error('Unknown issue action type : ' + action
);
237 return mediaRouterMojom
.Issue
.ActionType
.OK
;
241 var secondaryActions
= (issue
.secondaryActions
|| []).map(function(e
) {
242 return issueActionToMojo_(e
);
244 this.service_
.onIssue(new mediaRouterMojom
.Issue({
245 'route_id': issue
.routeId
,
246 'severity': issueSeverityToMojo_(issue
.severity
),
247 'title': issue
.title
,
248 'message': issue
.message
,
249 'default_action': issueActionToMojo_(issue
.defaultAction
),
250 'secondary_actions': secondaryActions
,
251 'help_url': issue
.helpUrl
,
252 'is_blocking': issue
.isBlocking
257 * Called by the provider manager when the set of active routes
259 * @param {!Array<MediaRoute>} routes The active set of media routes.
261 MediaRouter
.prototype.onRoutesUpdated = function(routes
) {
262 this.service_
.onRoutesUpdated(routes
.map(routeToMojo_
));
266 * Object containing callbacks set by the provider manager.
267 * TODO(mfoltz): Better named ProviderManagerDelegate?
272 function MediaRouterHandlers() {
274 * @type {function(!string, !string, !string, !string, !number}
276 this.createRoute
= null;
279 * @type {function(!string, !string, !string, !number)}
281 this.joinRoute
= null;
284 * @type {function(string)}
286 this.closeRoute
= null;
289 * @type {function(string)}
291 this.startObservingMediaSinks
= null;
294 * @type {function(string)}
296 this.stopObservingMediaSinks
= null;
299 * @type {function(string, string): Promise}
301 this.sendRouteMessage
= null;
304 * @type {function(string, Uint8Array): Promise}
306 this.sendRouteBinaryMessage
= null;
309 * @type {function(string):
310 * Promise.<{messages: Array.<RouteMessage>, error: boolean}>}
312 this.listenForRouteMessages
= null;
315 * @type {function(string)}
317 this.stopListeningForRouteMessages
= null;
320 * @type {function(string)}
322 this.onPresentationSessionDetached
= null;
327 this.startObservingMediaRoutes
= null;
332 this.stopObservingMediaRoutes
= null;
336 * Routes calls from Media Router to the provider manager extension.
337 * Registered with the MediaRouter stub.
338 * @param {!MediaRouter} MediaRouter proxy to call into the
339 * Media Router mojo interface.
342 function MediaRouteProvider(mediaRouter
) {
343 mediaRouterMojom
.MediaRouteProvider
.stubClass
.call(this);
346 * Object containing JS callbacks into Provider Manager code.
347 * @type {!MediaRouterHandlers}
349 this.handlers_
= new MediaRouterHandlers();
352 * Proxy class to the browser's Media Router Mojo service.
353 * @type {!MediaRouter}
355 this.mediaRouter_
= mediaRouter
;
357 MediaRouteProvider
.prototype = Object
.create(
358 mediaRouterMojom
.MediaRouteProvider
.stubClass
.prototype);
361 * Sets the callback handler used to invoke methods in the provider manager.
363 * TODO(mfoltz): Rename to something more explicit?
364 * @param {!MediaRouterHandlers} handlers
366 MediaRouteProvider
.prototype.setHandlers = function(handlers
) {
367 this.handlers_
= handlers
;
368 var requiredHandlers
= [
369 'stopObservingMediaRoutes',
370 'startObservingMediaRoutes',
372 'sendRouteBinaryMessage',
373 'listenForRouteMessages',
374 'stopListeningForRouteMessages',
375 'onPresentationSessionDetached',
379 'stopObservingMediaSinks',
380 'startObservingMediaRoutes'
382 requiredHandlers
.forEach(function(nextHandler
) {
383 if (handlers
[nextHandler
] === undefined) {
384 console
.error(nextHandler
+ ' handler not registered.');
390 * Starts querying for sinks capable of displaying the media source
391 * designated by |sourceUrn|. Results are returned by calling
393 * @param {!string} sourceUrn
395 MediaRouteProvider
.prototype.startObservingMediaSinks
=
396 function(sourceUrn
) {
397 this.handlers_
.startObservingMediaSinks(sourceUrn
);
401 * Stops querying for sinks capable of displaying |sourceUrn|.
402 * @param {!string} sourceUrn
404 MediaRouteProvider
.prototype.stopObservingMediaSinks
=
405 function(sourceUrn
) {
406 this.handlers_
.stopObservingMediaSinks(sourceUrn
);
410 * Requests that |sinkId| render the media referenced by |sourceUrn|. If the
411 * request is from the Presentation API, then origin and tabId will
413 * @param {!string} sourceUrn Media source to render.
414 * @param {!string} sinkId Media sink ID.
415 * @param {!string} presentationId Presentation ID from the site
416 * requesting presentation. TODO(mfoltz): Remove.
417 * @param {!string} origin Origin of site requesting presentation.
418 * @param {!number} tabId ID of tab requesting presentation.
419 * @return {!Promise.<!Object>} A Promise resolving to an object describing
420 * the newly created media route, or rejecting with an error message on
423 MediaRouteProvider
.prototype.createRoute
=
424 function(sourceUrn
, sinkId
, presentationId
, origin
, tabId
) {
425 return this.handlers_
.createRoute(
426 sourceUrn
, sinkId
, presentationId
, origin
, tabId
)
427 .then(function(route
) {
428 return {route
: routeToMojo_(route
)};
430 .catch(function(err
) {
431 return {error_text
: 'Error creating route: ' + err
.message
};
436 * Handles a request via the Presentation API to join an existing route given
437 * by |sourceUrn| and |presentationId|. |origin| and |tabId| are used for
438 * validating same-origin/tab scope.
439 * @param {!string} sourceUrn Media source to render.
440 * @param {!string} presentationId Presentation ID to join.
441 * @param {!string} origin Origin of site requesting join.
442 * @param {!number} tabId ID of tab requesting join.
443 * @return {!Promise.<!Object>} A Promise resolving to an object describing
444 * the newly created media route, or rejecting with an error message on
447 MediaRouteProvider
.prototype.joinRoute
=
448 function(sourceUrn
, presentationId
, origin
, tabId
) {
449 return this.handlers_
.joinRoute(sourceUrn
, presentationId
, origin
, tabId
)
450 .then(function(newRoute
) {
451 return {route
: routeToMojo_(newRoute
)};
454 return {error_text
: 'Error joining route: ' + err
.message
};
459 * Closes the route specified by |routeId|.
460 * @param {!string} routeId
462 MediaRouteProvider
.prototype.closeRoute = function(routeId
) {
463 this.handlers_
.closeRoute(routeId
);
467 * Posts a message to the route designated by |routeId|.
468 * @param {!string} routeId
469 * @param {!string} message
470 * @return {!Promise.<boolean>} Resolved with true if the message was sent,
471 * or false on failure.
473 MediaRouteProvider
.prototype.sendRouteMessage = function(
475 return this.handlers_
.sendRouteMessage(routeId
, message
)
477 return {'sent': true};
479 return {'sent': false};
484 * Sends a binary message to the route designated by |routeId|.
485 * @param {!string} routeId
486 * @param {!Uint8Array} data
487 * @return {!Promise.<boolean>} Resolved with true if the data was sent,
488 * or false on failure.
490 MediaRouteProvider
.prototype.sendRouteBinaryMessage = function(
492 return this.handlers_
.sendRouteBinaryMessage(routeId
, data
)
494 return {'sent': true};
496 return {'sent': false};
501 * Listen for next batch of messages from one of the routeIds.
502 * @param {!string} routeId
503 * @return {!Promise.<{messages: Array.<RouteMessage>, error: boolean}>}
504 * Resolved with a list of messages, and a boolean indicating if an error
507 MediaRouteProvider
.prototype.listenForRouteMessages = function(routeId
) {
508 return this.handlers_
.listenForRouteMessages(routeId
)
509 .then(function(messages
) {
510 return {'messages': messages
.map(messageToMojo_
), 'error': false};
512 return {'messages': [], 'error': true};
517 * If there is an outstanding |listenForRouteMessages| promise for
518 * |routeId|, resolve that promise with an empty array.
519 * @param {!string} routeId
521 MediaRouteProvider
.prototype.stopListeningForRouteMessages = function(
523 return this.handlers_
.stopListeningForRouteMessages(routeId
);
527 * Indicates that the presentation session that was connected to |routeId| is
528 * no longer connected to it.
529 * @param {!string} routeId
531 MediaRouteProvider
.prototype.onPresentationSessionDetached = function(
533 this.handlers_
.onPresentationSessionDetached(routeId
);
537 * Requests that the provider manager start sending information about active
538 * media routes to the Media Router.
540 MediaRouteProvider
.prototype.startObservingMediaRoutes = function() {
541 this.handlers_
.startObservingMediaRoutes();
545 * Requests that the provider manager stop sending information about active
546 * media routes to the Media Router.
548 MediaRouteProvider
.prototype.stopObservingMediaRoutes = function() {
549 this.handlers_
.stopObservingMediaRoutes();
552 mediaRouter
= new MediaRouter(connector
.bindHandleToProxy(
553 serviceProvider
.connectToService(
554 mediaRouterMojom
.MediaRouter
.name
),
555 mediaRouterMojom
.MediaRouter
));