blob: 96fb7c4598c30b424ac1f75c525afb10e5a03827 [file] [log] [blame]
// Copyright 2017 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/event_page_request_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/containers/circular_deque.h"
#include "base/logging.h"
#include "base/macros.h"
#include "extensions/browser/event_page_tracker.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_manager_factory.h"
namespace media_router {
EventPageRequestManager::~EventPageRequestManager() = default;
void EventPageRequestManager::Shutdown() {
event_page_tracker_ = nullptr;
}
void EventPageRequestManager::SetExtensionId(const std::string& extension_id) {
media_route_provider_extension_id_ = extension_id;
}
void EventPageRequestManager::RunOrDefer(
base::OnceClosure request,
MediaRouteProviderWakeReason wake_reason) {
if (mojo_connections_ready_) {
DCHECK(!media_route_provider_extension_id_.empty());
std::move(request).Run();
} else {
EnqueueRequest(std::move(request));
if (IsEventPageSuspended()) {
SetWakeReason(wake_reason);
AttemptWakeEventPage();
}
}
}
void EventPageRequestManager::OnMojoConnectionsReady() {
if (IsEventPageSuspended()) {
DVLOG(1)
<< "OnMojoConnectionsReady was called while extension is suspended.";
SetWakeReason(MediaRouteProviderWakeReason::REGISTER_MEDIA_ROUTE_PROVIDER);
AttemptWakeEventPage();
return;
}
mojo_connections_ready_ = true;
base::circular_deque<base::OnceClosure> requests;
requests.swap(pending_requests_);
for (base::OnceClosure& request : requests) {
DCHECK(mojo_connections_ready_);
// The requests should not queue additional requests when executed.
std::move(request).Run();
}
DCHECK(pending_requests_.empty());
wakeup_attempt_count_ = 0;
}
void EventPageRequestManager::OnMojoConnectionError() {
mojo_connections_ready_ = false;
// If this method is invoked while there are pending requests, then
// it means we tried to wake the extension, but weren't able to complete the
// connection to media route provider. Since we do not know whether the error
// is transient, reattempt the wakeup.
if (!pending_requests_.empty()) {
DLOG(ERROR) << "A connection error while there are pending requests.";
SetWakeReason(MediaRouteProviderWakeReason::CONNECTION_ERROR);
AttemptWakeEventPage();
}
}
EventPageRequestManager::EventPageRequestManager(
content::BrowserContext* context)
: event_page_tracker_(extensions::ProcessManager::Get(context)),
weak_factory_(this) {}
void EventPageRequestManager::EnqueueRequest(base::OnceClosure request) {
pending_requests_.push_back(std::move(request));
if (pending_requests_.size() > kMaxPendingRequests) {
DLOG(ERROR) << "Reached max queue size. Dropping oldest request.";
pending_requests_.pop_front();
}
DVLOG(2) << "EnqueueRequest (queue-length=" << pending_requests_.size()
<< ")";
}
void EventPageRequestManager::DrainPendingRequests() {
DLOG(ERROR) << "Draining request queue. (queue-length="
<< pending_requests_.size() << ")";
pending_requests_.clear();
}
void EventPageRequestManager::SetWakeReason(
MediaRouteProviderWakeReason reason) {
DCHECK(reason != MediaRouteProviderWakeReason::TOTAL_COUNT);
if (current_wake_reason_ == MediaRouteProviderWakeReason::TOTAL_COUNT)
current_wake_reason_ = reason;
}
bool EventPageRequestManager::IsEventPageSuspended() const {
return !event_page_tracker_ || event_page_tracker_->IsEventPageSuspended(
media_route_provider_extension_id_);
}
void EventPageRequestManager::AttemptWakeEventPage() {
++wakeup_attempt_count_;
if (wakeup_attempt_count_ > kMaxWakeupAttemptCount) {
DLOG(ERROR) << "Attempted too many times to wake up event page.";
DrainPendingRequests();
wakeup_attempt_count_ = 0;
MediaRouterMojoMetrics::RecordMediaRouteProviderWakeup(
MediaRouteProviderWakeup::ERROR_TOO_MANY_RETRIES);
return;
}
DVLOG(1) << "Attempting to wake up event page: attempt "
<< wakeup_attempt_count_;
if (!event_page_tracker_) {
DLOG(ERROR) << "Attempted to wake up event page without a valid event page"
"tracker";
return;
}
// This return false if the extension is already awake.
// Callback is bound using WeakPtr because |event_page_tracker_| outlives
// |this|.
if (!event_page_tracker_->WakeEventPage(
media_route_provider_extension_id_,
base::BindOnce(&EventPageRequestManager::OnWakeComplete,
weak_factory_.GetWeakPtr()))) {
DLOG(ERROR) << "Failed to schedule a wakeup for event page.";
}
}
void EventPageRequestManager::OnWakeComplete(bool success) {
// If there are multiple overlapping WakeEventPage requests, ensure the
// metrics are only recorded once.
if (current_wake_reason_ == MediaRouteProviderWakeReason::TOTAL_COUNT)
return;
if (success) {
MediaRouterMojoMetrics::RecordMediaRouteProviderWakeReason(
current_wake_reason_);
ClearWakeReason();
MediaRouterMojoMetrics::RecordMediaRouteProviderWakeup(
MediaRouteProviderWakeup::SUCCESS);
return;
}
// This is likely an non-retriable error. Drop the pending requests.
DLOG(ERROR) << "An error encountered while waking the event page.";
ClearWakeReason();
DrainPendingRequests();
MediaRouterMojoMetrics::RecordMediaRouteProviderWakeup(
MediaRouteProviderWakeup::ERROR_UNKNOWN);
}
void EventPageRequestManager::ClearWakeReason() {
DCHECK(current_wake_reason_ != MediaRouteProviderWakeReason::TOTAL_COUNT);
current_wake_reason_ = MediaRouteProviderWakeReason::TOTAL_COUNT;
}
} // namespace media_router