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"
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/issues_observer.h"
14 #include "chrome/browser/media/router/media_router_factory.h"
15 #include "chrome/browser/media/router/media_router_type_converters.h"
16 #include "chrome/browser/media/router/media_routes_observer.h"
17 #include "chrome/browser/media/router/media_sinks_observer.h"
18 #include "chrome/browser/media/router/presentation_session_messages_observer.h"
19 #include "extensions/browser/process_manager.h"
21 #define DVLOG_WITH_INSTANCE(level) \
22 DVLOG(level) << "MR #" << instance_id_ << ": "
24 #define DLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": "
26 namespace media_router
{
29 // Converts the callback result of calling Mojo CreateRoute()/JoinRoute()
30 // into a local callback.
31 void RouteResponseReceived(
32 const std::string
& presentation_id
,
33 const std::vector
<MediaRouteResponseCallback
>& callbacks
,
34 interfaces::MediaRoutePtr media_route
,
35 const mojo::String
& error_text
) {
36 scoped_ptr
<MediaRoute
> route
;
37 std::string actual_presentation_id
;
39 if (media_route
.is_null()) {
41 DCHECK(!error_text
.is_null());
42 error
= !error_text
.get().empty() ? error_text
.get() : "Unknown error.";
44 route
= media_route
.To
<scoped_ptr
<MediaRoute
>>();
45 actual_presentation_id
= presentation_id
;
48 for (const MediaRouteResponseCallback
& callback
: callbacks
)
49 callback
.Run(route
.get(), actual_presentation_id
, error
);
52 // TODO(imcheng): We should handle failure in this case. One way is to invoke
53 // all pending requests with failure. (crbug.com/490787)
54 void EventPageWakeComplete(bool success
) {
56 LOG(ERROR
) << "An error encountered while waking the event page.";
59 scoped_ptr
<content::PresentationSessionMessage
>
60 ConvertToPresentationSessionMessage(interfaces::RouteMessagePtr input
) {
61 DCHECK(!input
.is_null());
62 scoped_ptr
<content::PresentationSessionMessage
> output
;
63 switch (input
->type
) {
64 case interfaces::RouteMessage::Type::TYPE_TEXT
: {
65 DCHECK(!input
->message
.is_null());
66 DCHECK(input
->data
.is_null());
67 output
.reset(new content::PresentationSessionMessage(
68 content::PresentationMessageType::TEXT
));
69 input
->message
.Swap(&output
->message
);
72 case interfaces::RouteMessage::Type::TYPE_BINARY
: {
73 DCHECK(!input
->data
.is_null());
74 DCHECK(input
->message
.is_null());
75 output
.reset(new content::PresentationSessionMessage(
76 content::PresentationMessageType::ARRAY_BUFFER
));
77 output
->data
.reset(new std::vector
<uint8_t>);
78 input
->data
.Swap(output
->data
.get());
83 NOTREACHED() << "Invalid route message type " << input
->type
;
89 MediaRouterMojoImpl::MediaRouterMojoImpl(
90 extensions::EventPageTracker
* event_page_tracker
)
91 : event_page_tracker_(event_page_tracker
),
92 instance_id_(base::GenerateGUID()) {
93 DCHECK(event_page_tracker_
);
96 MediaRouterMojoImpl::~MediaRouterMojoImpl() {
97 DCHECK(thread_checker_
.CalledOnValidThread());
101 void MediaRouterMojoImpl::BindToRequest(
102 const std::string
& extension_id
,
103 content::BrowserContext
* context
,
104 mojo::InterfaceRequest
<interfaces::MediaRouter
> request
) {
105 MediaRouterMojoImpl
* impl
= static_cast<MediaRouterMojoImpl
*>(
106 MediaRouterFactory::GetApiForBrowserContext(context
));
109 impl
->BindToMojoRequest(request
.Pass(), extension_id
);
112 void MediaRouterMojoImpl::BindToMojoRequest(
113 mojo::InterfaceRequest
<interfaces::MediaRouter
> request
,
114 const std::string
& extension_id
) {
115 DCHECK(thread_checker_
.CalledOnValidThread());
118 new mojo::Binding
<interfaces::MediaRouter
>(this, request
.Pass()));
119 binding_
->set_connection_error_handler(base::Bind(
120 &MediaRouterMojoImpl::OnConnectionError
, base::Unretained(this)));
122 media_route_provider_extension_id_
= extension_id
;
125 // TODO(imcheng): If this occurs while there are pending requests, we should
126 // probably invoke them with failure. (crbug.com/490787)
127 void MediaRouterMojoImpl::OnConnectionError() {
128 DCHECK(thread_checker_
.CalledOnValidThread());
130 media_route_provider_
.reset();
134 void MediaRouterMojoImpl::RegisterMediaRouteProvider(
135 interfaces::MediaRouteProviderPtr media_route_provider_ptr
,
136 const interfaces::MediaRouter::RegisterMediaRouteProviderCallback
&
138 DCHECK(thread_checker_
.CalledOnValidThread());
140 media_route_provider_
= media_route_provider_ptr
.Pass();
141 media_route_provider_
.set_connection_error_handler(base::Bind(
142 &MediaRouterMojoImpl::OnConnectionError
, base::Unretained(this)));
143 callback
.Run(instance_id_
);
144 ExecutePendingRequests();
147 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue
) {
148 DCHECK(thread_checker_
.CalledOnValidThread());
149 DVLOG_WITH_INSTANCE(1) << "OnIssue " << issue
->title
;
150 const Issue
& issue_converted
= issue
.To
<Issue
>();
151 issue_manager_
.AddIssue(issue_converted
);
154 void MediaRouterMojoImpl::OnSinksReceived(
155 const mojo::String
& media_source
,
156 mojo::Array
<interfaces::MediaSinkPtr
> sinks
) {
157 DCHECK(thread_checker_
.CalledOnValidThread());
159 DVLOG_WITH_INSTANCE(1) << "OnSinksReceived";
160 std::vector
<MediaSink
> sinks_converted
;
161 sinks_converted
.reserve(sinks
.size());
163 for (size_t i
= 0; i
< sinks
.size(); ++i
) {
164 sinks_converted
.push_back(sinks
[i
].To
<MediaSink
>());
167 auto it
= sinks_observers_
.find(media_source
);
168 if (it
== sinks_observers_
.end()) {
169 DVLOG_WITH_INSTANCE(1)
170 << "Received sink list without any active observers: " << media_source
;
172 FOR_EACH_OBSERVER(MediaSinksObserver
, *it
->second
,
173 OnSinksReceived(sinks_converted
));
177 void MediaRouterMojoImpl::OnRoutesUpdated(
178 mojo::Array
<interfaces::MediaRoutePtr
> routes
) {
179 DCHECK(thread_checker_
.CalledOnValidThread());
181 DVLOG_WITH_INSTANCE(1) << "OnRoutesUpdated";
183 std::vector
<MediaRoute
> routes_converted
;
184 routes_converted
.reserve(routes
.size());
186 for (size_t i
= 0; i
< routes
.size(); ++i
) {
187 routes_converted
.push_back(routes
[i
].To
<MediaRoute
>());
190 FOR_EACH_OBSERVER(MediaRoutesObserver
, routes_observers_
,
191 OnRoutesUpdated(routes_converted
));
194 void MediaRouterMojoImpl::CreateRoute(
195 const MediaSource::Id
& source_id
,
196 const MediaSink::Id
& sink_id
,
199 const std::vector
<MediaRouteResponseCallback
>& callbacks
) {
200 DCHECK(thread_checker_
.CalledOnValidThread());
202 if (!origin
.is_valid()) {
203 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin
;
204 for (const MediaRouteResponseCallback
& callback
: callbacks
)
205 callback
.Run(nullptr, "", "Invalid origin");
208 RunOrDefer(base::Bind(
209 &MediaRouterMojoImpl::DoCreateRoute
, base::Unretained(this), source_id
,
210 sink_id
, origin
.is_empty() ? "" : origin
.spec(), tab_id
, callbacks
));
213 void MediaRouterMojoImpl::JoinRoute(
214 const MediaSource::Id
& source_id
,
215 const std::string
& presentation_id
,
218 const std::vector
<MediaRouteResponseCallback
>& callbacks
) {
219 DCHECK(thread_checker_
.CalledOnValidThread());
221 if (!origin
.is_valid()) {
222 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin
;
223 for (const MediaRouteResponseCallback
& callback
: callbacks
)
224 callback
.Run(nullptr, "", "Invalid origin");
227 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoJoinRoute
,
228 base::Unretained(this), source_id
, presentation_id
,
229 origin
.is_empty() ? "" : origin
.spec(), tab_id
,
233 void MediaRouterMojoImpl::CloseRoute(const MediaRoute::Id
& route_id
) {
234 DCHECK(thread_checker_
.CalledOnValidThread());
236 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCloseRoute
,
237 base::Unretained(this), route_id
));
240 void MediaRouterMojoImpl::SendRouteMessage(
241 const MediaRoute::Id
& route_id
,
242 const std::string
& message
,
243 const SendRouteMessageCallback
& callback
) {
244 DCHECK(thread_checker_
.CalledOnValidThread());
246 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionMessage
,
247 base::Unretained(this), route_id
, message
, callback
));
250 void MediaRouterMojoImpl::SendRouteBinaryMessage(
251 const MediaRoute::Id
& route_id
,
252 scoped_ptr
<std::vector
<uint8
>> data
,
253 const SendRouteMessageCallback
& callback
) {
254 DCHECK(thread_checker_
.CalledOnValidThread());
256 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionBinaryMessage
,
257 base::Unretained(this), route_id
,
258 base::Passed(data
.Pass()), callback
));
261 void MediaRouterMojoImpl::ClearIssue(const Issue::Id
& issue_id
) {
262 DCHECK(thread_checker_
.CalledOnValidThread());
263 issue_manager_
.ClearIssue(issue_id
);
264 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoClearIssue
,
265 base::Unretained(this), issue_id
));
268 void MediaRouterMojoImpl::OnPresentationSessionDetached(
269 const MediaRoute::Id
& route_id
) {
270 DCHECK(thread_checker_
.CalledOnValidThread());
271 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoOnPresentationSessionDetached
,
272 base::Unretained(this), route_id
));
275 void MediaRouterMojoImpl::RegisterMediaSinksObserver(
276 MediaSinksObserver
* observer
) {
277 DCHECK(thread_checker_
.CalledOnValidThread());
279 // Create an observer list for the media source and add |observer|
280 // to it. Fail if |observer| is already registered.
281 const std::string
& source_id
= observer
->source().id();
282 base::ObserverList
<MediaSinksObserver
>* observer_list
=
283 sinks_observers_
.get(source_id
);
284 if (!observer_list
) {
285 observer_list
= new base::ObserverList
<MediaSinksObserver
>;
286 sinks_observers_
.add(source_id
, make_scoped_ptr(observer_list
));
288 DCHECK(!observer_list
->HasObserver(observer
));
291 // We need to call DoStartObservingMediaSinks every time an observer is
292 // added to ensure the observer will be notified with a fresh set of results.
293 // TODO(imcheng): Implement caching. (crbug.com/492451)
294 observer_list
->AddObserver(observer
);
295 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks
,
296 base::Unretained(this), source_id
));
299 void MediaRouterMojoImpl::UnregisterMediaSinksObserver(
300 MediaSinksObserver
* observer
) {
301 DCHECK(thread_checker_
.CalledOnValidThread());
303 const MediaSource::Id
& source_id
= observer
->source().id();
304 auto* observer_list
= sinks_observers_
.get(source_id
);
305 if (!observer_list
|| !observer_list
->HasObserver(observer
)) {
309 // If we are removing the final observer for the source, then stop
310 // observing sinks for it.
311 // might_have_observers() is reliable here on the assumption that this call
312 // is not inside the ObserverList iteration.
313 observer_list
->RemoveObserver(observer
);
314 if (!observer_list
->might_have_observers()) {
315 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaSinks
,
316 base::Unretained(this), source_id
));
317 sinks_observers_
.erase(source_id
);
321 void MediaRouterMojoImpl::RegisterMediaRoutesObserver(
322 MediaRoutesObserver
* observer
) {
323 DCHECK(thread_checker_
.CalledOnValidThread());
324 DCHECK(!routes_observers_
.HasObserver(observer
));
326 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaRoutes
,
327 base::Unretained(this)));
328 routes_observers_
.AddObserver(observer
);
331 void MediaRouterMojoImpl::UnregisterMediaRoutesObserver(
332 MediaRoutesObserver
* observer
) {
333 if (!routes_observers_
.HasObserver(observer
))
336 routes_observers_
.RemoveObserver(observer
);
337 if (!routes_observers_
.might_have_observers()) {
338 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaRoutes
,
339 base::Unretained(this)));
343 void MediaRouterMojoImpl::RegisterIssuesObserver(IssuesObserver
* observer
) {
344 DCHECK(thread_checker_
.CalledOnValidThread());
345 issue_manager_
.RegisterObserver(observer
);
348 void MediaRouterMojoImpl::UnregisterIssuesObserver(IssuesObserver
* observer
) {
349 DCHECK(thread_checker_
.CalledOnValidThread());
350 issue_manager_
.UnregisterObserver(observer
);
353 void MediaRouterMojoImpl::RegisterPresentationSessionMessagesObserver(
354 PresentationSessionMessagesObserver
* observer
) {
355 DCHECK(thread_checker_
.CalledOnValidThread());
357 const MediaRoute::Id
& route_id
= observer
->route_id();
358 auto* observer_list
= messages_observers_
.get(route_id
);
359 if (!observer_list
) {
360 observer_list
= new base::ObserverList
<PresentationSessionMessagesObserver
>;
361 messages_observers_
.add(route_id
, make_scoped_ptr(observer_list
));
363 DCHECK(!observer_list
->HasObserver(observer
));
366 bool should_listen
= !observer_list
->might_have_observers();
367 observer_list
->AddObserver(observer
);
369 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoListenForRouteMessages
,
370 base::Unretained(this), route_id
));
374 void MediaRouterMojoImpl::UnregisterPresentationSessionMessagesObserver(
375 PresentationSessionMessagesObserver
* observer
) {
376 DCHECK(thread_checker_
.CalledOnValidThread());
379 const MediaRoute::Id
& route_id
= observer
->route_id();
380 auto* observer_list
= messages_observers_
.get(route_id
);
381 if (!observer_list
|| !observer_list
->HasObserver(observer
))
384 observer_list
->RemoveObserver(observer
);
385 if (!observer_list
->might_have_observers()) {
386 messages_observers_
.erase(route_id
);
387 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopListeningForRouteMessages
,
388 base::Unretained(this), route_id
));
392 void MediaRouterMojoImpl::DoCreateRoute(
393 const MediaSource::Id
& source_id
,
394 const MediaSink::Id
& sink_id
,
395 const std::string
& origin
,
397 const std::vector
<MediaRouteResponseCallback
>& callbacks
) {
398 std::string
presentation_id("mr_");
399 presentation_id
+= base::GenerateGUID();
400 DVLOG_WITH_INSTANCE(1) << "DoCreateRoute " << source_id
<< "=>" << sink_id
401 << ", presentation ID: " << presentation_id
;
402 media_route_provider_
->CreateRoute(
403 source_id
, sink_id
, presentation_id
, origin
, tab_id
,
404 base::Bind(&RouteResponseReceived
, presentation_id
, callbacks
));
407 void MediaRouterMojoImpl::DoJoinRoute(
408 const MediaSource::Id
& source_id
,
409 const std::string
& presentation_id
,
410 const std::string
& origin
,
412 const std::vector
<MediaRouteResponseCallback
>& callbacks
) {
413 DVLOG_WITH_INSTANCE(1) << "DoJoinRoute " << source_id
414 << ", presentation ID: " << presentation_id
;
415 media_route_provider_
->JoinRoute(
416 source_id
, presentation_id
, origin
, tab_id
,
417 base::Bind(&RouteResponseReceived
, presentation_id
, callbacks
));
420 void MediaRouterMojoImpl::DoCloseRoute(const MediaRoute::Id
& route_id
) {
421 DVLOG_WITH_INSTANCE(1) << "DoCloseRoute " << route_id
;
422 media_route_provider_
->CloseRoute(route_id
);
425 void MediaRouterMojoImpl::DoSendSessionMessage(
426 const MediaRoute::Id
& route_id
,
427 const std::string
& message
,
428 const SendRouteMessageCallback
& callback
) {
429 DVLOG_WITH_INSTANCE(1) << "SendRouteMessage " << route_id
;
430 media_route_provider_
->SendRouteMessage(route_id
, message
, callback
);
433 void MediaRouterMojoImpl::DoSendSessionBinaryMessage(
434 const MediaRoute::Id
& route_id
,
435 scoped_ptr
<std::vector
<uint8
>> data
,
436 const SendRouteMessageCallback
& callback
) {
437 DVLOG_WITH_INSTANCE(1) << "SendRouteBinaryMessage " << route_id
;
438 mojo::Array
<uint8
> mojo_array
;
439 mojo_array
.Swap(data
.get());
440 media_route_provider_
->SendRouteBinaryMessage(route_id
, mojo_array
.Pass(),
444 void MediaRouterMojoImpl::DoListenForRouteMessages(
445 const MediaRoute::Id
& route_id
) {
446 DVLOG_WITH_INSTANCE(1) << "ListenForRouteMessages";
447 if (!ContainsValue(route_ids_listening_for_messages_
, route_id
)) {
448 route_ids_listening_for_messages_
.insert(route_id
);
449 media_route_provider_
->ListenForRouteMessages(
450 route_id
, base::Bind(&MediaRouterMojoImpl::OnRouteMessagesReceived
,
451 base::Unretained(this), route_id
));
455 void MediaRouterMojoImpl::DoStopListeningForRouteMessages(
456 const MediaRoute::Id
& route_id
) {
457 DVLOG_WITH_INSTANCE(1) << "StopListeningForRouteMessages";
459 // No need to erase |route_ids_listening_for_messages_| entry here.
460 // It will be removed when there are no more observers by the time
461 // |OnRouteMessagesReceived| is invoked.
462 media_route_provider_
->StopListeningForRouteMessages(route_id
);
465 void MediaRouterMojoImpl::OnRouteMessagesReceived(
466 const MediaRoute::Id
& route_id
,
467 mojo::Array
<interfaces::RouteMessagePtr
> messages
,
469 DVLOG(1) << "OnRouteMessagesReceived";
471 // If |messages| is null, then no more messages will come from this route.
472 // We can stop listening.
474 DVLOG(2) << "Encountered error in OnRouteMessagesReceived for " << route_id
;
475 route_ids_listening_for_messages_
.erase(route_id
);
479 // Check if there are any observers remaining. If not, the messages
480 // can be discarded and we can stop listening for the next batch of messages.
481 auto* observer_list
= messages_observers_
.get(route_id
);
482 if (!observer_list
) {
483 route_ids_listening_for_messages_
.erase(route_id
);
487 // If |messages| is empty, then |StopListeningForRouteMessages| was invoked
488 // but we have added back an observer since. Keep listening for more messages,
489 // but do not notify observers with empty list.
490 if (!messages
.storage().empty()) {
491 ScopedVector
<content::PresentationSessionMessage
> session_messages
;
492 session_messages
.reserve(messages
.size());
493 for (size_t i
= 0; i
< messages
.size(); ++i
) {
494 session_messages
.push_back(
495 ConvertToPresentationSessionMessage(messages
[i
].Pass()).Pass());
497 base::ObserverList
<PresentationSessionMessagesObserver
>::Iterator
498 observer_it(observer_list
);
499 bool single_observer
=
500 observer_it
.GetNext() != nullptr && observer_it
.GetNext() == nullptr;
501 FOR_EACH_OBSERVER(PresentationSessionMessagesObserver
, *observer_list
,
502 OnMessagesReceived(session_messages
, single_observer
));
505 // Listen for more messages.
506 media_route_provider_
->ListenForRouteMessages(
507 route_id
, base::Bind(&MediaRouterMojoImpl::OnRouteMessagesReceived
,
508 base::Unretained(this), route_id
));
511 void MediaRouterMojoImpl::DoClearIssue(const Issue::Id
& issue_id
) {
512 DVLOG_WITH_INSTANCE(1) << "DoClearIssue " << issue_id
;
513 media_route_provider_
->ClearIssue(issue_id
);
516 void MediaRouterMojoImpl::DoOnPresentationSessionDetached(
517 const MediaRoute::Id
& route_id
) {
518 DVLOG_WITH_INSTANCE(1) << "DoOnPresentationSessionDetached " << route_id
;
519 media_route_provider_
->OnPresentationSessionDetached(route_id
);
522 void MediaRouterMojoImpl::DoStartObservingMediaSinks(
523 const MediaSource::Id
& source_id
) {
524 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaSinks: " << source_id
;
525 media_route_provider_
->StartObservingMediaSinks(source_id
);
528 void MediaRouterMojoImpl::DoStopObservingMediaSinks(
529 const MediaSource::Id
& source_id
) {
530 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaSinks: " << source_id
;
531 media_route_provider_
->StopObservingMediaSinks(source_id
);
534 void MediaRouterMojoImpl::DoStartObservingMediaRoutes() {
535 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaRoutes";
536 media_route_provider_
->StartObservingMediaRoutes();
539 void MediaRouterMojoImpl::DoStopObservingMediaRoutes() {
540 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaRoutes";
541 media_route_provider_
->StopObservingMediaRoutes();
544 void MediaRouterMojoImpl::EnqueueTask(const base::Closure
& closure
) {
545 pending_requests_
.push_back(closure
);
546 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (queue-length="
547 << pending_requests_
.size() << ")";
550 void MediaRouterMojoImpl::RunOrDefer(const base::Closure
& request
) {
551 DCHECK(event_page_tracker_
);
553 if (media_route_provider_extension_id_
.empty()) {
554 DVLOG_WITH_INSTANCE(1) << "Extension ID not known yet.";
555 EnqueueTask(request
);
556 } else if (event_page_tracker_
->IsEventPageSuspended(
557 media_route_provider_extension_id_
)) {
558 DVLOG_WITH_INSTANCE(1) << "Waking event page.";
559 EnqueueTask(request
);
560 if (!event_page_tracker_
->WakeEventPage(
561 media_route_provider_extension_id_
,
562 base::Bind(&EventPageWakeComplete
))) {
563 LOG(ERROR
) << "An error encountered while waking the event page.";
565 media_route_provider_
.reset();
566 } else if (!media_route_provider_
) {
567 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting ProvideMediaRouter "
569 EnqueueTask(request
);
575 void MediaRouterMojoImpl::ExecutePendingRequests() {
576 DCHECK(thread_checker_
.CalledOnValidThread());
577 DCHECK(media_route_provider_
);
578 DCHECK(event_page_tracker_
);
579 DCHECK(!media_route_provider_extension_id_
.empty());
581 if (event_page_tracker_
->IsEventPageSuspended(
582 media_route_provider_extension_id_
)) {
583 DVLOG_WITH_INSTANCE(1)
584 << "ExecutePendingRequests was called while extension is suspended.";
588 for (const auto& next_request
: pending_requests_
)
591 pending_requests_
.clear();
594 } // namespace media_router