Chromium Code Reviews| Index: chrome/browser/media/router/media_router_mojo_impl.cc |
| diff --git a/chrome/browser/media/router/media_router_mojo_impl.cc b/chrome/browser/media/router/media_router_mojo_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2754b92b67d7f5cd6376e684ebee01bc88c22530 |
| --- /dev/null |
| +++ b/chrome/browser/media/router/media_router_mojo_impl.cc |
| @@ -0,0 +1,384 @@ |
| +// 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. |
| + |
| +#include "chrome/browser/media/router/media_router_mojo_impl.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/guid.h" |
| +#include "base/logging.h" |
| +#include "base/observer_list.h" |
| +#include "chrome/browser/media/router/media_router_impl.h" |
| +#include "chrome/browser/media/router/media_router_impl_factory.h" |
| +#include "chrome/browser/media/router/media_router_mojo_impl_factory.h" |
| +#include "chrome/browser/media/router/media_router_type_converters.h" |
| +#include "chrome/browser/media/router/media_routes_observer.h" |
| +#include "chrome/browser/media/router/media_sinks_observer.h" |
| +#include "extensions/browser/process_manager.h" |
| + |
| +#define DVLOG_WITH_INSTANCE(level) \ |
| + DVLOG(level) << "MR #" << instance_id_ << ": " |
| + |
| +namespace media_router { |
| +namespace { |
| + |
| +// Converts the callback result of calling Mojo CreateRoute() into a local |
| +// callback. |
| +void CreateRouteFinished(const MediaSinkId& sink_id, |
| + const MediaRouteResponseCallback& callback, |
| + interfaces::MediaRoutePtr media_route, |
| + const mojo::String& error_text) { |
| + if (media_route.is_null()) { |
| + // An error occurred. |
| + DCHECK(!error_text.is_null()); |
| + callback.Run(nullptr, (!error_text.get().empty() ? error_text.get() |
|
mark a. foltz
2015/05/08 00:58:37
extra ()
Kevin M
2015/05/12 23:56:09
Done.
|
| + : "Unknown error.")); |
| + return; |
| + } |
| + callback.Run(make_scoped_ptr(new MediaRoute(media_route.To<MediaRoute>())), |
| + ""); |
| +} |
| + |
| +void EventPageWakeComplete(bool success) { |
| + if (!success) |
| + LOG(ERROR) << "An error encountered while waking the event page."; |
| +} |
| + |
| +} // namespace |
| + |
| +MediaRouterMojoImpl::MediaRouterMojoImpl() |
| + : event_page_tracker_(nullptr), instance_id_(base::GenerateGUID()) { |
| +} |
| + |
| +MediaRouterMojoImpl::MediaRouterMojoImpl( |
| + const std::string& mrpm_extension_id, |
| + extensions::EventPageTracker* event_page_tracker_for_test) |
| + : MediaRouterMojoImpl() { |
| + DCHECK(!mrpm_extension_id.empty()); |
| + DCHECK(event_page_tracker_for_test); |
| + mrpm_extension_id_ = mrpm_extension_id; |
| + event_page_tracker_ = event_page_tracker_for_test; |
| +} |
| + |
| +MediaRouterMojoImpl::~MediaRouterMojoImpl() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| +} |
| + |
| +// static |
| +void MediaRouterMojoImpl::BindToRequest( |
| + const std::string& extension_id, |
| + content::BrowserContext* context, |
| + mojo::InterfaceRequest<interfaces::MediaRouterObserver> request) { |
| + MediaRouterMojoImpl* impl = |
| + MediaRouterMojoImplFactory::GetApiForBrowserContext(context); |
| + DCHECK(impl); |
| + impl->Bind(request.Pass()); |
|
mark a. foltz
2015/05/08 00:58:37
What does this do?
Kevin M
2015/05/12 23:56:09
Binds the object "impl" to a Mojo interface reques
|
| + impl->MonitorExtension(extension_id, context); |
|
mark a. foltz
2015/05/08 00:58:37
What if extension_id is already being monitored or
Kevin M
2015/05/12 23:56:09
It is idempotent for a given extension ID and cont
|
| +} |
| + |
| +void MediaRouterMojoImpl::Bind( |
| + mojo::InterfaceRequest<interfaces::MediaRouterObserver> request) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + binding_.reset( |
| + new mojo::Binding<interfaces::MediaRouterObserver>(this, request.Pass())); |
| + binding_->set_error_handler(this); |
| +} |
| + |
| +void MediaRouterMojoImpl::OnConnectionError() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + mrpm_.reset(); |
| + binding_.reset(); |
| +} |
| + |
| +void MediaRouterMojoImpl::ProvideMediaRouter( |
| + interfaces::MediaRouterPtr mrpm, |
| + const interfaces::MediaRouterObserver::ProvideMediaRouterCallback& |
| + callback) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + mrpm_ = mrpm.Pass(); |
| + mrpm_.set_error_handler(this); |
| + callback.Run(instance_id_); |
| + ExecutePendingRequests(); |
| +} |
| + |
| +void MediaRouterMojoImpl::OnMessage(const mojo::String& route_id, |
| + const mojo::String& message) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + NOTIMPLEMENTED(); |
| +} |
| + |
| +void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + NOTIMPLEMENTED(); |
| +} |
| + |
| +void MediaRouterMojoImpl::OnSinksReceived( |
| + const mojo::String& media_source, |
| + mojo::Array<interfaces::MediaSinkPtr> sinks) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DVLOG_WITH_INSTANCE(1) << "OnSinksReceived"; |
| + std::vector<MediaSink> sinks_converted; |
| + sinks_converted.reserve(sinks.size()); |
| + |
| + for (size_t i = 0; i < sinks.size(); ++i) { |
| + sinks_converted.push_back(sinks[i].To<MediaSink>()); |
| + } |
| + |
| + auto it = sinks_observers_.find(media_source); |
| + if (it == sinks_observers_.end()) { |
| + DVLOG_WITH_INSTANCE(1) |
| + << "Received sink list without any active observers: " << media_source; |
| + } else { |
| + FOR_EACH_OBSERVER(MediaSinksObserver, *it->second, |
| + OnSinksReceived(sinks_converted)); |
| + } |
| +} |
| + |
| +void MediaRouterMojoImpl::OnRoutesUpdated( |
| + mojo::Array<interfaces::MediaRoutePtr> routes) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DVLOG_WITH_INSTANCE(1) << "OnRoutesUpdated"; |
| + |
| + std::vector<MediaRoute> routes_converted; |
| + routes_converted.reserve(routes.size()); |
| + |
| + for (size_t i = 0; i < routes.size(); ++i) { |
| + routes_converted.push_back(routes[i].To<MediaRoute>()); |
| + } |
| + |
| + FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_, |
| + OnRoutesUpdated(routes_converted)); |
| +} |
| + |
| +// ---------------------------------------------------------------------------- |
| +// MediaRouter methods. |
| + |
| +void MediaRouterMojoImpl::CreateRoute( |
| + const MediaSourceId& source_id, |
| + const MediaSinkId& sink_id, |
| + const MediaRouteResponseCallback& callback) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCreateRoute, |
| + base::Unretained(this), source_id, sink_id, callback)); |
| +} |
| + |
| +void MediaRouterMojoImpl::CloseRoute(const MediaRouteId& route_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCloseRoute, |
| + base::Unretained(this), route_id)); |
| +} |
| + |
| +void MediaRouterMojoImpl::PostMessage(const MediaRouteId& route_id, |
| + const std::string& message) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoPostMessage, |
| + base::Unretained(this), route_id, message)); |
| +} |
| + |
| +void MediaRouterMojoImpl::ClearIssue(const Issue::IssueId& issue_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoClearIssue, |
| + base::Unretained(this), issue_id)); |
| +} |
| + |
| +bool MediaRouterMojoImpl::RegisterMediaSinksObserver( |
| + MediaSinksObserver* observer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // Lazily create an observer list for the media source and add |observer| |
|
mark a. foltz
2015/05/08 00:58:37
This doesn't look lazy - you're newing one up at L
Kevin M
2015/05/12 23:56:09
Done.
|
| + // to it. |
| + const std::string& source_id = observer->source().id(); |
| + linked_ptr<ObserverList<MediaSinksObserver>> observer_list = |
| + sinks_observers_[source_id]; |
| + if (!observer_list.get()) { |
| + observer_list = make_linked_ptr(new ObserverList<MediaSinksObserver>); |
| + sinks_observers_[source_id] = observer_list; |
| + } else { |
| + if (observer_list->HasObserver(observer)) { |
| + DLOG(FATAL) << "Redundant RegisterMediaSinksObserver call detected."; |
|
mark a. foltz
2015/05/08 00:58:38
Log instance id?
Kevin M
2015/05/12 23:56:09
Done.
|
| + return true; |
| + } |
| + } |
| + |
| + // Turn on sink observation if this is the first time we've started observing |
| + // sinks for the media source. |
| + if (!observer_list->might_have_observers()) { |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks, |
| + base::Unretained(this), source_id)); |
| + } |
| + observer_list->AddObserver(observer); |
| + |
| + return true; |
| +} |
| + |
| +void MediaRouterMojoImpl::UnregisterMediaSinksObserver( |
| + MediaSinksObserver* observer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + const std::string& source_id = observer->source().id(); |
| + const auto observer_list_it = sinks_observers_.find(source_id); |
| + if (observer_list_it == sinks_observers_.end() || |
| + !observer_list_it->second->HasObserver(observer)) { |
| + return; |
| + } |
| + observer_list_it->second->RemoveObserver(observer); |
| + |
| + // If we are removing the final observer for the source, then stop |
| + // observing sinks for it. |
| + if (!observer_list_it->second->might_have_observers()) { |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaSinks, |
| + base::Unretained(this), source_id)); |
| + sinks_observers_.erase(observer_list_it); |
| + } |
| +} |
| + |
| +bool MediaRouterMojoImpl::RegisterMediaRoutesObserver( |
| + MediaRoutesObserver* observer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + if (routes_observers_.HasObserver(observer)) { |
| + DLOG(FATAL) << "Redundant RegisterMediaRoutesObserver() call detected."; |
| + return true; |
| + } |
| + |
| + // TODO(kmarshall): add result caching and max limits. |
| + if (!routes_observers_.might_have_observers()) { |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaRoutes, |
| + base::Unretained(this))); |
| + } |
| + routes_observers_.AddObserver(observer); |
| + return true; |
| +} |
| + |
| +void MediaRouterMojoImpl::UnregisterMediaRoutesObserver( |
| + MediaRoutesObserver* observer) { |
| + if (!routes_observers_.HasObserver(observer)) { |
| + return; |
| + } |
| + routes_observers_.RemoveObserver(observer); |
| + if (!routes_observers_.might_have_observers()) { |
| + RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaRoutes, |
| + base::Unretained(this))); |
| + } |
| +} |
| + |
| +void MediaRouterMojoImpl::AddIssuesObserver(IssuesObserver* observer) { |
| + NOTIMPLEMENTED(); // TODO(kmarshall): To be landed in a separate CL. |
|
mark a. foltz
2015/05/08 00:58:37
I think that's implied by NOTIMPLEMENTED() :)
Or
Kevin M
2015/05/12 23:56:09
Done.
|
| +} |
| + |
| +void MediaRouterMojoImpl::RemoveIssuesObserver(IssuesObserver* observer) { |
| + NOTIMPLEMENTED(); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoCreateRoute( |
| + const MediaSourceId& source_id, |
| + const MediaSinkId& sink_id, |
| + const MediaRouteResponseCallback& callback) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "CreateRoute " << source_id << "=>" << sink_id; |
| + mrpm_->CreateRoute(source_id, sink_id, |
| + base::Bind(&CreateRouteFinished, sink_id, callback)); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoCloseRoute(const MediaRouteId& route_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "CloseRoute " << route_id; |
| + mrpm_->CloseRoute(route_id); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoPostMessage(const MediaRouteId& route_id, |
| + const std::string& message) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "PostMessage " << route_id; |
| + mrpm_->PostMessage(route_id, message); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoClearIssue(const Issue::IssueId& issue_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "ClearIssue " << issue_id; |
| + mrpm_->ClearIssue(issue_id); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoStartObservingMediaSinks( |
| + const std::string& source_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "StartObservingMediaSinks: " << source_id; |
| + mrpm_->StartObservingMediaSinks(source_id); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoStopObservingMediaSinks( |
| + const std::string& source_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "StopObservingMediaSinks: " << source_id; |
| + mrpm_->StopObservingMediaSinks(source_id); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoStartObservingMediaRoutes() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "StartObservingMediaRoutes"; |
| + mrpm_->StartObservingMediaRoutes(); |
| +} |
| + |
| +void MediaRouterMojoImpl::DoStopObservingMediaRoutes() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DVLOG_WITH_INSTANCE(1) << "StopObservingMediaRoutes"; |
| + mrpm_->StopObservingMediaRoutes(); |
| +} |
| + |
| +void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) { |
| + DVLOG_WITH_INSTANCE(2) << "EnqueueTask (size=" << pending_requests_.size() |
| + << ")"; |
| + pending_requests_.push_back(closure); |
| +} |
| + |
| +void MediaRouterMojoImpl::MonitorExtension(const std::string& extension_id, |
| + content::BrowserContext* context) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(!extension_id.empty()); |
| + DCHECK(context); |
| + mrpm_extension_id_ = extension_id; |
| + event_page_tracker_ = extensions::ProcessManager::Get(context); |
| +} |
| + |
| +void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request_cb) { |
| + DCHECK(event_page_tracker_); |
| + if (event_page_tracker_->IsEventPageSuspended(mrpm_extension_id_)) { |
| + DVLOG_WITH_INSTANCE(1) << "Waking event page."; |
| + pending_requests_.push_back(request_cb); |
| + if (!event_page_tracker_->WakeEventPage( |
| + mrpm_extension_id_, base::Bind(&EventPageWakeComplete))) { |
| + LOG(ERROR) << "An error encountered while waking the event page."; |
| + } |
| + mrpm_.reset(); |
| + } else if (!mrpm_) { |
| + DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting MRPM connection."; |
| + pending_requests_.push_back(request_cb); |
| + } else { |
| + request_cb.Run(); |
| + } |
| +} |
| + |
| +void MediaRouterMojoImpl::ExecutePendingRequests() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(mrpm_); |
| + DCHECK(event_page_tracker_); |
| + if (event_page_tracker_->IsEventPageSuspended(mrpm_extension_id_)) { |
| + DVLOG_WITH_INSTANCE(1) |
| + << "ExecutePendingRequests was called while extension is suspended."; |
| + return; |
| + } |
| + |
| + for (const auto& next_request : pending_requests_) { |
| + next_request.Run(); |
| + } |
| + pending_requests_.clear(); |
| +} |
| + |
| +} // namespace media_router |