blob: ad89b527353fb2e38d1bf48f949550d1d7992cda [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/compute_pressure/pressure_service_base.h"
#include <utility>
#include "base/ranges/algorithm.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/video_picture_in_picture_window_controller.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
namespace content {
PressureServiceBase::PressureServiceBase()
: source_to_client_{PressureClientImpl(this)} {}
PressureServiceBase::~PressureServiceBase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
// https://www.w3.org/TR/compute-pressure/#dfn-document-has-implicit-focus
bool PressureServiceBase::HasImplicitFocus(RenderFrameHost* render_frame_host) {
// 1. If document is not fully active, return false.
if (!render_frame_host->IsActive()) {
return false;
}
WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
WebContents::FromRenderFrameHost(render_frame_host));
const auto& current_origin = render_frame_host->GetLastCommittedOrigin();
// 3. If associated document is same origin with initiators of active
// Picture-in-Picture sessions, return true.
if (base::ranges::any_of(
WebContentsImpl::GetAllWebContents(), [&](WebContentsImpl* wc) {
if (!wc->HasPictureInPictureVideo()) {
return false;
}
auto origin = PictureInPictureWindowController::
GetOrCreateVideoPictureInPictureController(wc)
->GetOrigin();
return current_origin == origin.value() &&
wc->GetBrowserContext() == web_contents->GetBrowserContext();
})) {
return true;
}
// 4. If browsing context is capturing, return true.
// TODO(crbug.com/40945930): Take muted state into account.
if (static_cast<RenderFrameHostImpl*>(render_frame_host)
->HasMediaStreams(
RenderFrameHostImpl::MediaStreamType::kCapturingMediaStream)) {
return true;
}
// 6. If top-level browsing context does not have system focus, return false.
RenderWidgetHostImpl* rwh = static_cast<RenderWidgetHostImpl*>(
render_frame_host->GetRenderWidgetHost());
if (!rwh->is_focused()) {
return false;
}
// 7. Let focused document be the currently focused area's node document.
auto* focused_frame = web_contents->GetFocusedFrame();
if (!focused_frame) {
return false;
}
// 8. If origin is same origin with focused document, return true.
// 9. Otherwise, return false.
return current_origin.IsSameOriginWith(
focused_frame->GetLastCommittedOrigin());
}
bool PressureServiceBase::CanCallAddClient() const {
return true;
}
void PressureServiceBase::BindReceiver(
mojo::PendingReceiver<blink::mojom::WebPressureManager> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (manager_receiver_.is_bound()) {
mojo::ReportBadMessage("PressureService is already connected.");
return;
}
manager_receiver_.Bind(std::move(receiver));
// base::Unretained is safe because Mojo guarantees the callback will not
// be called after `manager_receiver_` is deallocated, and `manager_receiver_`
// is owned by this class.
manager_receiver_.set_disconnect_handler(
base::BindRepeating(&PressureServiceBase::OnPressureManagerDisconnected,
base::Unretained(this)));
}
void PressureServiceBase::AddClient(device::mojom::PressureSource source,
AddClientCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!CanCallAddClient()) {
std::move(callback).Run(
device::mojom::PressureManagerAddClientResult::NewError(
device::mojom::PressureManagerAddClientError::kNotSupported));
return;
}
auto& pressure_client = source_to_client_[static_cast<size_t>(source)];
if (pressure_client.is_client_remote_bound()) {
manager_receiver_.ReportBadMessage(
"PressureClientImpl is already connected.");
return;
}
if (!manager_remote_.is_bound()) {
auto receiver = manager_remote_.BindNewPipeAndPassReceiver();
// base::Unretained is safe because Mojo guarantees the callback will not
// be called after `manager_remote_` is deallocated, and `manager_remote_`
// is owned by this class.
manager_remote_.set_disconnect_handler(
base::BindRepeating(&PressureServiceBase::OnPressureManagerDisconnected,
base::Unretained(this)));
GetDeviceService().BindPressureManager(std::move(receiver));
}
if (pressure_client.is_client_receiver_bound()) {
// Calling BindNewPipeAndPassReceiver() is safe because we call
// PressureClientImpl::is_client_remote_bound() above.
std::move(callback).Run(
device::mojom::PressureManagerAddClientResult::NewPressureClient(
pressure_client.BindNewPipeAndPassReceiver()));
} else {
manager_remote_->AddClient(
source, GetTokenFor(source),
base::BindOnce(&PressureServiceBase::DidAddClient,
weak_ptr_factory_.GetWeakPtr(), source,
std::move(callback)));
}
}
// Disconnection handler for |manager_receiver_| and |manager_remote_|. If
// either of the connections breaks, we should disconnect all connections and
// let //services know we do not need more updates.
void PressureServiceBase::OnPressureManagerDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
manager_receiver_.reset();
manager_remote_.reset();
}
void PressureServiceBase::DidAddClient(
device::mojom::PressureSource source,
AddClientCallback client_callback,
device::mojom::PressureManagerAddClientResultPtr result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result->is_error()) {
std::move(client_callback).Run(std::move(result));
return;
}
auto& pressure_client = source_to_client_[static_cast<size_t>(source)];
pressure_client.BindReceiver(std::move(result->get_pressure_client()));
std::move(client_callback)
.Run(device::mojom::PressureManagerAddClientResult::NewPressureClient(
// This is safe because AddClient() already checked
// PressureClientImpl::is_client_remote_bound()'s return value.
pressure_client.BindNewPipeAndPassReceiver()));
}
} // namespace content