Convert browser_tests to Swarming.
[chromium-blink-merge.git] / chrome / browser / media / router / media_router_mojo_impl.cc
blobaab17bb30bed8e07cfe4b19cc903858bf521f0c8
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.
5 #include "chrome/browser/media/router/media_router_mojo_impl.h"
7 #include "base/bind.h"
8 #include "base/guid.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/observer_list.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/media/router/media_router_mojo_impl_factory.h"
14 #include "chrome/browser/media/router/media_router_type_converters.h"
15 #include "chrome/browser/media/router/media_routes_observer.h"
16 #include "chrome/browser/media/router/media_sinks_observer.h"
17 #include "extensions/browser/process_manager.h"
19 #define DVLOG_WITH_INSTANCE(level) \
20 DVLOG(level) << "MR #" << instance_id_ << ": "
22 #define DLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": "
24 namespace media_router {
25 namespace {
27 // Converts the callback result of calling Mojo CreateRoute()/JoinRoute()
28 // into a local callback.
29 void RouteResponseReceived(const MediaRouteResponseCallback& callback,
30 interfaces::MediaRoutePtr media_route,
31 const mojo::String& error_text) {
32 if (media_route.is_null()) {
33 // An error occurred.
34 DCHECK(!error_text.is_null());
35 callback.Run(nullptr, !error_text.get().empty() ? error_text.get()
36 : "Unknown error.");
37 return;
39 callback.Run(media_route.To<scoped_ptr<MediaRoute>>(), "");
42 // TODO(imcheng): We should handle failure in this case. One way is to invoke
43 // all pending requests with failure. (crbug.com/490787)
44 void EventPageWakeComplete(bool success) {
45 if (!success)
46 LOG(ERROR) << "An error encountered while waking the event page.";
49 scoped_ptr<content::PresentationSessionMessage>
50 ConvertToPresentationSessionMessage(interfaces::RouteMessagePtr input) {
51 DCHECK(!input.is_null());
52 // TODO(haibinlu): get presentation_url&id from route_id
53 std::string presentation_url;
54 std::string presentation_id;
55 scoped_ptr<content::PresentationSessionMessage> output;
56 switch (input->type) {
57 case interfaces::RouteMessage::Type::TYPE_TEXT: {
58 DCHECK(!input->message.is_null());
59 DCHECK(input->data.is_null());
60 output = content::PresentationSessionMessage::CreateStringMessage(
61 presentation_url, presentation_id, make_scoped_ptr(new std::string));
62 input->message.Swap(output->message.get());
63 return output.Pass();
65 case interfaces::RouteMessage::Type::TYPE_BINARY: {
66 DCHECK(!input->data.is_null());
67 DCHECK(input->message.is_null());
68 output = content::PresentationSessionMessage::CreateArrayBufferMessage(
69 presentation_url, presentation_id,
70 make_scoped_ptr(new std::vector<uint8_t>));
71 input->data.Swap(output->data.get());
72 return output.Pass();
76 NOTREACHED() << "Invalid route message type " << input->type;
77 return output.Pass();
80 } // namespace
82 MediaRouterMojoImpl::MediaRouterMojoImpl(
83 extensions::EventPageTracker* event_page_tracker)
84 : event_page_tracker_(event_page_tracker),
85 instance_id_(base::GenerateGUID()) {
86 DCHECK(event_page_tracker_);
89 MediaRouterMojoImpl::~MediaRouterMojoImpl() {
90 DCHECK(thread_checker_.CalledOnValidThread());
93 // static
94 void MediaRouterMojoImpl::BindToRequest(
95 const std::string& extension_id,
96 content::BrowserContext* context,
97 mojo::InterfaceRequest<interfaces::MediaRouterObserver> request) {
98 MediaRouterMojoImpl* impl =
99 MediaRouterMojoImplFactory::GetApiForBrowserContext(context);
100 DCHECK(impl);
102 impl->BindToMojoRequest(request.Pass(), extension_id);
105 void MediaRouterMojoImpl::BindToMojoRequest(
106 mojo::InterfaceRequest<interfaces::MediaRouterObserver> request,
107 const std::string& extension_id) {
108 DCHECK(thread_checker_.CalledOnValidThread());
110 binding_.reset(
111 new mojo::Binding<interfaces::MediaRouterObserver>(this, request.Pass()));
112 binding_->set_error_handler(this);
114 mojo_media_router_extension_id_ = extension_id;
117 // TODO(imcheng): If this occurs while there are pending requests, we should
118 // probably invoke them with failure. (crbug.com/490787)
119 void MediaRouterMojoImpl::OnConnectionError() {
120 DCHECK(thread_checker_.CalledOnValidThread());
122 mojo_media_router_.reset();
123 binding_.reset();
126 void MediaRouterMojoImpl::ProvideMediaRouter(
127 interfaces::MediaRouterPtr media_router_ptr,
128 const interfaces::MediaRouterObserver::ProvideMediaRouterCallback&
129 callback) {
130 DCHECK(thread_checker_.CalledOnValidThread());
132 mojo_media_router_ = media_router_ptr.Pass();
133 mojo_media_router_.set_error_handler(this);
134 callback.Run(instance_id_);
135 ExecutePendingRequests();
138 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) {
139 // TODO(imcheng): Implement. (crbug.com/461815)
140 DCHECK(thread_checker_.CalledOnValidThread());
141 NOTIMPLEMENTED();
144 void MediaRouterMojoImpl::OnSinksReceived(
145 const mojo::String& media_source,
146 mojo::Array<interfaces::MediaSinkPtr> sinks) {
147 DCHECK(thread_checker_.CalledOnValidThread());
149 DVLOG_WITH_INSTANCE(1) << "OnSinksReceived";
150 std::vector<MediaSink> sinks_converted;
151 sinks_converted.reserve(sinks.size());
153 for (size_t i = 0; i < sinks.size(); ++i) {
154 sinks_converted.push_back(sinks[i].To<MediaSink>());
157 auto it = sinks_observers_.find(media_source);
158 if (it == sinks_observers_.end()) {
159 DVLOG_WITH_INSTANCE(1)
160 << "Received sink list without any active observers: " << media_source;
161 } else {
162 FOR_EACH_OBSERVER(MediaSinksObserver, *it->second,
163 OnSinksReceived(sinks_converted));
167 void MediaRouterMojoImpl::OnRoutesUpdated(
168 mojo::Array<interfaces::MediaRoutePtr> routes) {
169 DCHECK(thread_checker_.CalledOnValidThread());
171 DVLOG_WITH_INSTANCE(1) << "OnRoutesUpdated";
173 std::vector<MediaRoute> routes_converted;
174 routes_converted.reserve(routes.size());
176 for (size_t i = 0; i < routes.size(); ++i) {
177 routes_converted.push_back(routes[i].To<MediaRoute>());
180 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_,
181 OnRoutesUpdated(routes_converted));
184 void MediaRouterMojoImpl::CreateRoute(
185 const MediaSource::Id& source_id,
186 const MediaSink::Id& sink_id,
187 const GURL& origin,
188 int tab_id,
189 const MediaRouteResponseCallback& callback) {
190 DCHECK(thread_checker_.CalledOnValidThread());
192 if (!origin.is_valid()) {
193 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
194 callback.Run(nullptr, "Invalid origin");
195 return;
197 RunOrDefer(base::Bind(
198 &MediaRouterMojoImpl::DoCreateRoute, base::Unretained(this), source_id,
199 sink_id, origin.is_empty() ? "" : origin.spec(), tab_id, callback));
202 void MediaRouterMojoImpl::JoinRoute(
203 const MediaSource::Id& source_id,
204 const std::string& presentation_id,
205 const GURL& origin,
206 int tab_id,
207 const MediaRouteResponseCallback& callback) {
208 DCHECK(thread_checker_.CalledOnValidThread());
210 if (!origin.is_valid()) {
211 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
212 callback.Run(nullptr, "Invalid origin");
213 return;
215 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoJoinRoute,
216 base::Unretained(this), source_id, presentation_id,
217 origin.is_empty() ? "" : origin.spec(), tab_id,
218 callback));
221 void MediaRouterMojoImpl::CloseRoute(const MediaRoute::Id& route_id) {
222 DCHECK(thread_checker_.CalledOnValidThread());
224 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCloseRoute,
225 base::Unretained(this), route_id));
228 void MediaRouterMojoImpl::SendRouteMessage(
229 const MediaRoute::Id& route_id,
230 const std::string& message,
231 const SendRouteMessageCallback& callback) {
232 DCHECK(thread_checker_.CalledOnValidThread());
234 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionMessage,
235 base::Unretained(this), route_id, message, callback));
238 void MediaRouterMojoImpl::ListenForRouteMessages(
239 const std::vector<MediaRoute::Id>& route_ids,
240 const PresentationSessionMessageCallback& message_cb) {
241 DCHECK(thread_checker_.CalledOnValidThread());
242 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoListenForRouteMessages,
243 base::Unretained(this), route_ids, message_cb));
246 void MediaRouterMojoImpl::ClearIssue(const Issue::Id& issue_id) {
247 DCHECK(thread_checker_.CalledOnValidThread());
249 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoClearIssue,
250 base::Unretained(this), issue_id));
253 void MediaRouterMojoImpl::RegisterMediaSinksObserver(
254 MediaSinksObserver* observer) {
255 DCHECK(thread_checker_.CalledOnValidThread());
257 // Create an observer list for the media source and add |observer|
258 // to it. Fail if |observer| is already registered.
259 const std::string& source_id = observer->source().id();
260 base::ObserverList<MediaSinksObserver>* observer_list =
261 sinks_observers_.get(source_id);
262 if (!observer_list) {
263 observer_list = new base::ObserverList<MediaSinksObserver>;
264 sinks_observers_.add(source_id, make_scoped_ptr(observer_list));
265 } else {
266 DCHECK(!observer_list->HasObserver(observer));
269 // We need to call DoStartObservingMediaSinks every time an observer is
270 // added to ensure the observer will be notified with a fresh set of results.
271 // TODO(imcheng): Implement caching. (crbug.com/492451)
272 observer_list->AddObserver(observer);
273 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks,
274 base::Unretained(this), source_id));
277 void MediaRouterMojoImpl::UnregisterMediaSinksObserver(
278 MediaSinksObserver* observer) {
279 DCHECK(thread_checker_.CalledOnValidThread());
281 const std::string& source_id = observer->source().id();
282 auto* observer_list = sinks_observers_.get(source_id);
283 if (!observer_list || !observer_list->HasObserver(observer)) {
284 return;
287 // If we are removing the final observer for the source, then stop
288 // observing sinks for it.
289 // might_have_observers() is reliable here on the assumption that this call
290 // is not inside the ObserverList iteration.
291 observer_list->RemoveObserver(observer);
292 if (!observer_list->might_have_observers()) {
293 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaSinks,
294 base::Unretained(this), source_id));
295 sinks_observers_.erase(source_id);
299 void MediaRouterMojoImpl::RegisterMediaRoutesObserver(
300 MediaRoutesObserver* observer) {
301 DCHECK(thread_checker_.CalledOnValidThread());
302 DCHECK(!routes_observers_.HasObserver(observer));
304 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaRoutes,
305 base::Unretained(this)));
306 routes_observers_.AddObserver(observer);
309 void MediaRouterMojoImpl::UnregisterMediaRoutesObserver(
310 MediaRoutesObserver* observer) {
311 if (!routes_observers_.HasObserver(observer))
312 return;
314 routes_observers_.RemoveObserver(observer);
315 if (!routes_observers_.might_have_observers()) {
316 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaRoutes,
317 base::Unretained(this)));
321 void MediaRouterMojoImpl::RegisterIssuesObserver(IssuesObserver* observer) {
322 // TODO(imcheng): Implement. (crbug.com/461815)
323 NOTIMPLEMENTED();
326 void MediaRouterMojoImpl::UnregisterIssuesObserver(IssuesObserver* observer) {
327 // TODO(imcheng): Implement. (crbug.com/461815)
328 NOTIMPLEMENTED();
331 void MediaRouterMojoImpl::DoCreateRoute(
332 const MediaSource::Id& source_id,
333 const MediaSink::Id& sink_id,
334 const std::string& origin,
335 int tab_id,
336 const MediaRouteResponseCallback& callback) {
337 std::string presentation_id("mr_");
338 presentation_id += base::GenerateGUID();
339 DVLOG_WITH_INSTANCE(1) << "DoCreateRoute " << source_id << "=>" << sink_id
340 << ", presentation ID: " << presentation_id;
341 mojo_media_router_->CreateRoute(source_id, sink_id, presentation_id, origin,
342 tab_id,
343 base::Bind(&RouteResponseReceived, callback));
346 void MediaRouterMojoImpl::DoJoinRoute(
347 const MediaSource::Id& source_id,
348 const std::string& presentation_id,
349 const std::string& origin,
350 int tab_id,
351 const MediaRouteResponseCallback& callback) {
352 DVLOG_WITH_INSTANCE(1) << "DoJoinRoute " << source_id
353 << ", presentation ID: " << presentation_id;
354 mojo_media_router_->JoinRoute(source_id, presentation_id, origin, tab_id,
355 base::Bind(&RouteResponseReceived, callback));
358 void MediaRouterMojoImpl::DoCloseRoute(const MediaRoute::Id& route_id) {
359 DVLOG_WITH_INSTANCE(1) << "DoCloseRoute " << route_id;
360 mojo_media_router_->CloseRoute(route_id);
363 void MediaRouterMojoImpl::DoSendSessionMessage(
364 const MediaRoute::Id& route_id,
365 const std::string& message,
366 const SendRouteMessageCallback& callback) {
367 DVLOG_WITH_INSTANCE(1) << "SendRouteMessage " << route_id;
368 mojo_media_router_->SendRouteMessage(route_id, message, callback);
371 void MediaRouterMojoImpl::DoListenForRouteMessages(
372 const std::vector<MediaRoute::Id>& route_ids,
373 const PresentationSessionMessageCallback& message_cb) {
374 DVLOG_WITH_INSTANCE(1) << "ListenForRouteMessages";
375 mojo_media_router_->ListenForRouteMessages(
376 mojo::Array<mojo::String>::From(route_ids),
377 base::Bind(&MediaRouterMojoImpl::OnRouteMessageReceived,
378 base::Unretained(this), message_cb));
381 void MediaRouterMojoImpl::OnRouteMessageReceived(
382 const PresentationSessionMessageCallback& message_cb,
383 mojo::Array<interfaces::RouteMessagePtr> messages) {
384 scoped_ptr<ScopedVector<content::PresentationSessionMessage>>
385 session_messages(new ScopedVector<content::PresentationSessionMessage>());
386 for (size_t i = 0; i < messages.size(); ++i) {
387 session_messages->push_back(
388 ConvertToPresentationSessionMessage(messages[i].Pass()).Pass());
390 message_cb.Run(session_messages.Pass());
393 void MediaRouterMojoImpl::DoClearIssue(const Issue::Id& issue_id) {
394 DVLOG_WITH_INSTANCE(1) << "DoClearIssue " << issue_id;
395 mojo_media_router_->ClearIssue(issue_id);
398 void MediaRouterMojoImpl::DoStartObservingMediaSinks(
399 const MediaSource::Id& source_id) {
400 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaSinks: " << source_id;
401 mojo_media_router_->StartObservingMediaSinks(source_id);
404 void MediaRouterMojoImpl::DoStopObservingMediaSinks(
405 const MediaSource::Id& source_id) {
406 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaSinks: " << source_id;
407 mojo_media_router_->StopObservingMediaSinks(source_id);
410 void MediaRouterMojoImpl::DoStartObservingMediaRoutes() {
411 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaRoutes";
412 mojo_media_router_->StartObservingMediaRoutes();
415 void MediaRouterMojoImpl::DoStopObservingMediaRoutes() {
416 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaRoutes";
417 mojo_media_router_->StopObservingMediaRoutes();
420 void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) {
421 pending_requests_.push_back(closure);
422 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (queue-length="
423 << pending_requests_.size() << ")";
426 void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request) {
427 DCHECK(event_page_tracker_);
429 if (mojo_media_router_extension_id_.empty()) {
430 DVLOG_WITH_INSTANCE(1) << "Extension ID not known yet.";
431 EnqueueTask(request);
432 } else if (event_page_tracker_->IsEventPageSuspended(
433 mojo_media_router_extension_id_)) {
434 DVLOG_WITH_INSTANCE(1) << "Waking event page.";
435 EnqueueTask(request);
436 if (!event_page_tracker_->WakeEventPage(
437 mojo_media_router_extension_id_,
438 base::Bind(&EventPageWakeComplete))) {
439 LOG(ERROR) << "An error encountered while waking the event page.";
441 mojo_media_router_.reset();
442 } else if (!mojo_media_router_) {
443 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting ProvideMediaRouter "
444 " to be called.";
445 EnqueueTask(request);
446 } else {
447 request.Run();
451 void MediaRouterMojoImpl::ExecutePendingRequests() {
452 DCHECK(thread_checker_.CalledOnValidThread());
453 DCHECK(mojo_media_router_);
454 DCHECK(event_page_tracker_);
455 DCHECK(!mojo_media_router_extension_id_.empty());
457 if (event_page_tracker_->IsEventPageSuspended(
458 mojo_media_router_extension_id_)) {
459 DVLOG_WITH_INSTANCE(1)
460 << "ExecutePendingRequests was called while extension is suspended.";
461 return;
464 for (const auto& next_request : pending_requests_)
465 next_request.Run();
467 pending_requests_.clear();
470 } // namespace media_router