| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/media/router/media_router_mojo_impl.h" | 5 #include "chrome/browser/media/router/media_router_mojo_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/guid.h" | 8 #include "base/guid.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/scoped_vector.h" | 10 #include "base/memory/scoped_vector.h" |
| 11 #include "base/observer_list.h" | 11 #include "base/observer_list.h" |
| 12 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
| 13 #include "chrome/browser/media/router/issues_observer.h" | 13 #include "chrome/browser/media/router/issues_observer.h" |
| 14 #include "chrome/browser/media/router/local_media_routes_observer.h" | 14 #include "chrome/browser/media/router/local_media_routes_observer.h" |
| 15 #include "chrome/browser/media/router/media_router_factory.h" | 15 #include "chrome/browser/media/router/media_router_factory.h" |
| 16 #include "chrome/browser/media/router/media_router_type_converters.h" | 16 #include "chrome/browser/media/router/media_router_type_converters.h" |
| 17 #include "chrome/browser/media/router/media_routes_observer.h" | 17 #include "chrome/browser/media/router/media_routes_observer.h" |
| 18 #include "chrome/browser/media/router/media_sinks_observer.h" | 18 #include "chrome/browser/media/router/media_sinks_observer.h" |
| 19 #include "chrome/browser/media/router/presentation_session_messages_observer.h" | 19 #include "chrome/browser/media/router/presentation_session_messages_observer.h" |
| 20 #include "extensions/browser/process_manager.h" | 20 #include "extensions/browser/process_manager.h" |
| 21 | 21 |
| 22 #define DVLOG_WITH_INSTANCE(level) \ | 22 #define DVLOG_WITH_INSTANCE(level) \ |
| 23 DVLOG(level) << "MR #" << instance_id_ << ": " | 23 DVLOG(level) << "MR #" << instance_id_ << ": " |
| 24 | 24 |
| 25 #define DLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": " | 25 #define DLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": " |
| 26 | 26 |
| 27 namespace media_router { | 27 namespace media_router { |
| 28 namespace { | 28 namespace { |
| 29 | 29 |
| 30 // TODO(imcheng): We should handle failure in this case. One way is to invoke | |
| 31 // all pending requests with failure. (crbug.com/490787) | |
| 32 void EventPageWakeComplete(bool success) { | |
| 33 if (!success) | |
| 34 LOG(ERROR) << "An error encountered while waking the event page."; | |
| 35 } | |
| 36 | |
| 37 scoped_ptr<content::PresentationSessionMessage> | 30 scoped_ptr<content::PresentationSessionMessage> |
| 38 ConvertToPresentationSessionMessage(interfaces::RouteMessagePtr input) { | 31 ConvertToPresentationSessionMessage(interfaces::RouteMessagePtr input) { |
| 39 DCHECK(!input.is_null()); | 32 DCHECK(!input.is_null()); |
| 40 scoped_ptr<content::PresentationSessionMessage> output; | 33 scoped_ptr<content::PresentationSessionMessage> output; |
| 41 switch (input->type) { | 34 switch (input->type) { |
| 42 case interfaces::RouteMessage::Type::TYPE_TEXT: { | 35 case interfaces::RouteMessage::Type::TYPE_TEXT: { |
| 43 DCHECK(!input->message.is_null()); | 36 DCHECK(!input->message.is_null()); |
| 44 DCHECK(input->data.is_null()); | 37 DCHECK(input->data.is_null()); |
| 45 output.reset(new content::PresentationSessionMessage( | 38 output.reset(new content::PresentationSessionMessage( |
| 46 content::PresentationMessageType::TEXT)); | 39 content::PresentationMessageType::TEXT)); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 // |this| will be deleted in UpdateHasLocalRoute() if |has_local_route| is | 79 // |this| will be deleted in UpdateHasLocalRoute() if |has_local_route| is |
| 87 // false. Note that ObserverList supports removing an observer while | 80 // false. Note that ObserverList supports removing an observer while |
| 88 // iterating through it. | 81 // iterating through it. |
| 89 router_->UpdateHasLocalRoute(has_local_route); | 82 router_->UpdateHasLocalRoute(has_local_route); |
| 90 } | 83 } |
| 91 | 84 |
| 92 MediaRouterMojoImpl::MediaRouterMojoImpl( | 85 MediaRouterMojoImpl::MediaRouterMojoImpl( |
| 93 extensions::EventPageTracker* event_page_tracker) | 86 extensions::EventPageTracker* event_page_tracker) |
| 94 : event_page_tracker_(event_page_tracker), | 87 : event_page_tracker_(event_page_tracker), |
| 95 instance_id_(base::GenerateGUID()), | 88 instance_id_(base::GenerateGUID()), |
| 96 has_local_route_(false) { | 89 has_local_route_(false), |
| 90 wakeup_attempt_count_(0), |
| 91 weak_factory_(this) { |
| 97 DCHECK(event_page_tracker_); | 92 DCHECK(event_page_tracker_); |
| 98 } | 93 } |
| 99 | 94 |
| 100 MediaRouterMojoImpl::~MediaRouterMojoImpl() { | 95 MediaRouterMojoImpl::~MediaRouterMojoImpl() { |
| 101 DCHECK(thread_checker_.CalledOnValidThread()); | 96 DCHECK(thread_checker_.CalledOnValidThread()); |
| 102 | 97 |
| 103 // Make sure |routes_observer_| is destroyed first, because it triggers | 98 // Make sure |routes_observer_| is destroyed first, because it triggers |
| 104 // additional cleanup logic in this class that depends on other fields. | 99 // additional cleanup logic in this class that depends on other fields. |
| 105 routes_observer_.reset(); | 100 routes_observer_.reset(); |
| 106 } | 101 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 123 DCHECK(thread_checker_.CalledOnValidThread()); | 118 DCHECK(thread_checker_.CalledOnValidThread()); |
| 124 | 119 |
| 125 binding_.reset( | 120 binding_.reset( |
| 126 new mojo::Binding<interfaces::MediaRouter>(this, request.Pass())); | 121 new mojo::Binding<interfaces::MediaRouter>(this, request.Pass())); |
| 127 binding_->set_connection_error_handler(base::Bind( | 122 binding_->set_connection_error_handler(base::Bind( |
| 128 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this))); | 123 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this))); |
| 129 | 124 |
| 130 media_route_provider_extension_id_ = extension_id; | 125 media_route_provider_extension_id_ = extension_id; |
| 131 } | 126 } |
| 132 | 127 |
| 133 // TODO(imcheng): If this occurs while there are pending requests, we should | |
| 134 // probably invoke them with failure. (crbug.com/490787) | |
| 135 void MediaRouterMojoImpl::OnConnectionError() { | 128 void MediaRouterMojoImpl::OnConnectionError() { |
| 136 DCHECK(thread_checker_.CalledOnValidThread()); | 129 DCHECK(thread_checker_.CalledOnValidThread()); |
| 137 | 130 |
| 138 media_route_provider_.reset(); | 131 media_route_provider_.reset(); |
| 139 binding_.reset(); | 132 binding_.reset(); |
| 133 |
| 134 // If |OnConnectionError| is invoked while there are pending requests, then |
| 135 // it means we tried to wake the extension, but weren't able to complete the |
| 136 // connection to media route provider. Since we do not know whether the error |
| 137 // is transient, reattempt the wakeup. |
| 138 if (!pending_requests_.empty()) { |
| 139 DLOG_WITH_INSTANCE(ERROR) << "A connection error while there are pending " |
| 140 "requests."; |
| 141 AttemptWakeEventPage(); |
| 142 } |
| 140 } | 143 } |
| 141 | 144 |
| 142 void MediaRouterMojoImpl::RegisterMediaRouteProvider( | 145 void MediaRouterMojoImpl::RegisterMediaRouteProvider( |
| 143 interfaces::MediaRouteProviderPtr media_route_provider_ptr, | 146 interfaces::MediaRouteProviderPtr media_route_provider_ptr, |
| 144 const interfaces::MediaRouter::RegisterMediaRouteProviderCallback& | 147 const interfaces::MediaRouter::RegisterMediaRouteProviderCallback& |
| 145 callback) { | 148 callback) { |
| 146 DCHECK(thread_checker_.CalledOnValidThread()); | 149 DCHECK(thread_checker_.CalledOnValidThread()); |
| 147 | 150 |
| 151 if (event_page_tracker_->IsEventPageSuspended( |
| 152 media_route_provider_extension_id_)) { |
| 153 DVLOG_WITH_INSTANCE(1) |
| 154 << "ExecutePendingRequests was called while extension is suspended."; |
| 155 media_route_provider_.reset(); |
| 156 AttemptWakeEventPage(); |
| 157 return; |
| 158 } |
| 159 |
| 148 media_route_provider_ = media_route_provider_ptr.Pass(); | 160 media_route_provider_ = media_route_provider_ptr.Pass(); |
| 149 media_route_provider_.set_connection_error_handler(base::Bind( | 161 media_route_provider_.set_connection_error_handler(base::Bind( |
| 150 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this))); | 162 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this))); |
| 151 callback.Run(instance_id_); | 163 callback.Run(instance_id_); |
| 152 ExecutePendingRequests(); | 164 ExecutePendingRequests(); |
| 165 wakeup_attempt_count_ = 0; |
| 153 } | 166 } |
| 154 | 167 |
| 155 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) { | 168 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) { |
| 156 DCHECK(thread_checker_.CalledOnValidThread()); | 169 DCHECK(thread_checker_.CalledOnValidThread()); |
| 157 DVLOG_WITH_INSTANCE(1) << "OnIssue " << issue->title; | 170 DVLOG_WITH_INSTANCE(1) << "OnIssue " << issue->title; |
| 158 const Issue& issue_converted = issue.To<Issue>(); | 171 const Issue& issue_converted = issue.To<Issue>(); |
| 159 issue_manager_.AddIssue(issue_converted); | 172 issue_manager_.AddIssue(issue_converted); |
| 160 } | 173 } |
| 161 | 174 |
| 162 void MediaRouterMojoImpl::OnSinksReceived( | 175 void MediaRouterMojoImpl::OnSinksReceived( |
| (...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 607 media_route_provider_->StartObservingMediaRoutes(); | 620 media_route_provider_->StartObservingMediaRoutes(); |
| 608 } | 621 } |
| 609 | 622 |
| 610 void MediaRouterMojoImpl::DoStopObservingMediaRoutes() { | 623 void MediaRouterMojoImpl::DoStopObservingMediaRoutes() { |
| 611 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaRoutes"; | 624 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaRoutes"; |
| 612 media_route_provider_->StopObservingMediaRoutes(); | 625 media_route_provider_->StopObservingMediaRoutes(); |
| 613 } | 626 } |
| 614 | 627 |
| 615 void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) { | 628 void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) { |
| 616 pending_requests_.push_back(closure); | 629 pending_requests_.push_back(closure); |
| 630 if (pending_requests_.size() > kMaxPendingRequests) { |
| 631 DLOG_WITH_INSTANCE(ERROR) << "Reached max queue size. Dropping oldest " |
| 632 << "request."; |
| 633 pending_requests_.pop_front(); |
| 634 } |
| 617 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (queue-length=" | 635 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (queue-length=" |
| 618 << pending_requests_.size() << ")"; | 636 << pending_requests_.size() << ")"; |
| 619 } | 637 } |
| 620 | 638 |
| 621 void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request) { | 639 void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request) { |
| 622 DCHECK(event_page_tracker_); | 640 DCHECK(event_page_tracker_); |
| 623 | 641 |
| 624 if (media_route_provider_extension_id_.empty()) { | 642 if (media_route_provider_extension_id_.empty()) { |
| 625 DVLOG_WITH_INSTANCE(1) << "Extension ID not known yet."; | 643 DVLOG_WITH_INSTANCE(1) << "Extension ID not known yet."; |
| 626 EnqueueTask(request); | 644 EnqueueTask(request); |
| 627 } else if (event_page_tracker_->IsEventPageSuspended( | 645 } else if (event_page_tracker_->IsEventPageSuspended( |
| 628 media_route_provider_extension_id_)) { | 646 media_route_provider_extension_id_)) { |
| 629 DVLOG_WITH_INSTANCE(1) << "Waking event page."; | 647 DVLOG_WITH_INSTANCE(1) << "Waking event page."; |
| 630 EnqueueTask(request); | 648 EnqueueTask(request); |
| 631 if (!event_page_tracker_->WakeEventPage( | 649 AttemptWakeEventPage(); |
| 632 media_route_provider_extension_id_, | |
| 633 base::Bind(&EventPageWakeComplete))) { | |
| 634 LOG(ERROR) << "An error encountered while waking the event page."; | |
| 635 } | |
| 636 media_route_provider_.reset(); | 650 media_route_provider_.reset(); |
| 637 } else if (!media_route_provider_) { | 651 } else if (!media_route_provider_) { |
| 638 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting ProvideMediaRouter " | 652 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting ProvideMediaRouter " |
| 639 " to be called."; | 653 " to be called."; |
| 640 EnqueueTask(request); | 654 EnqueueTask(request); |
| 641 } else { | 655 } else { |
| 642 request.Run(); | 656 request.Run(); |
| 643 } | 657 } |
| 644 } | 658 } |
| 645 | 659 |
| 660 void MediaRouterMojoImpl::AttemptWakeEventPage() { |
| 661 if (wakeup_attempt_count_ >= kMaxWakeupAttemptCount) { |
| 662 DLOG_WITH_INSTANCE(ERROR) << "Attempted too many times to wake up event " |
| 663 << "page."; |
| 664 DrainPendingRequests(); |
| 665 wakeup_attempt_count_ = 0; |
| 666 return; |
| 667 } |
| 668 |
| 669 ++wakeup_attempt_count_; |
| 670 DVLOG_WITH_INSTANCE(1) << "Attempting to wake up event page: attempt " |
| 671 << wakeup_attempt_count_; |
| 672 |
| 673 // This return false if the extension is already awake. |
| 674 // Callback is bound using WeakPtr because |event_page_tracker_| outlives |
| 675 // |this|. |
| 676 if (!event_page_tracker_->WakeEventPage( |
| 677 media_route_provider_extension_id_, |
| 678 base::Bind(&MediaRouterMojoImpl::EventPageWakeComplete, |
| 679 weak_factory_.GetWeakPtr()))) { |
| 680 DLOG_WITH_INSTANCE(ERROR) << "Failed to schedule a wakeup for event page."; |
| 681 } |
| 682 } |
| 683 |
| 646 void MediaRouterMojoImpl::ExecutePendingRequests() { | 684 void MediaRouterMojoImpl::ExecutePendingRequests() { |
| 647 DCHECK(thread_checker_.CalledOnValidThread()); | 685 DCHECK(thread_checker_.CalledOnValidThread()); |
| 648 DCHECK(media_route_provider_); | 686 DCHECK(media_route_provider_); |
| 649 DCHECK(event_page_tracker_); | 687 DCHECK(event_page_tracker_); |
| 650 DCHECK(!media_route_provider_extension_id_.empty()); | 688 DCHECK(!media_route_provider_extension_id_.empty()); |
| 651 | 689 |
| 652 if (event_page_tracker_->IsEventPageSuspended( | |
| 653 media_route_provider_extension_id_)) { | |
| 654 DVLOG_WITH_INSTANCE(1) | |
| 655 << "ExecutePendingRequests was called while extension is suspended."; | |
| 656 return; | |
| 657 } | |
| 658 | |
| 659 for (const auto& next_request : pending_requests_) | 690 for (const auto& next_request : pending_requests_) |
| 660 next_request.Run(); | 691 next_request.Run(); |
| 661 | 692 |
| 662 pending_requests_.clear(); | 693 pending_requests_.clear(); |
| 663 } | 694 } |
| 664 | 695 |
| 696 void MediaRouterMojoImpl::EventPageWakeComplete(bool success) { |
| 697 if (success) |
| 698 return; |
| 699 |
| 700 // This is likely an non-retriable error. Drop the pending requests. |
| 701 DLOG_WITH_INSTANCE(ERROR) |
| 702 << "An error encountered while waking the event page."; |
| 703 DrainPendingRequests(); |
| 704 } |
| 705 |
| 706 void MediaRouterMojoImpl::DrainPendingRequests() { |
| 707 DLOG_WITH_INSTANCE(ERROR) |
| 708 << "Draining request queue. (queue-length=" << pending_requests_.size() |
| 709 << ")"; |
| 710 pending_requests_.clear(); |
| 711 } |
| 712 |
| 665 } // namespace media_router | 713 } // namespace media_router |
| OLD | NEW |