| // Copyright 2021 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 "content/browser/compute_pressure/pressure_service_impl.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/memory/ptr_util.h" | 
 | #include "content/public/browser/device_service.h" | 
 | #include "content/public/browser/render_frame_host.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "mojo/public/cpp/bindings/message.h" | 
 | #include "services/network/public/cpp/is_potentially_trustworthy.h" | 
 | #include "third_party/blink/public/common/features.h" | 
 | #include "third_party/blink/public/mojom/compute_pressure/pressure_service.mojom.h" | 
 | #include "third_party/blink/public/mojom/page/page_visibility_state.mojom.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | constexpr base::TimeDelta PressureServiceImpl::kDefaultVisibleObserverRateLimit; | 
 |  | 
 | // static | 
 | void PressureServiceImpl::Create( | 
 |     RenderFrameHost* render_frame_host, | 
 |     mojo::PendingReceiver<blink::mojom::PressureService> receiver) { | 
 |   if (!network::IsOriginPotentiallyTrustworthy( | 
 |           render_frame_host->GetLastCommittedOrigin())) { | 
 |     mojo::ReportBadMessage("Compute Pressure access from an insecure origin"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!GetForCurrentDocument(render_frame_host)) { | 
 |     CreateForCurrentDocument(render_frame_host, | 
 |                              kDefaultVisibleObserverRateLimit); | 
 |   } | 
 |  | 
 |   GetForCurrentDocument(render_frame_host)->BindReceiver(std::move(receiver)); | 
 | } | 
 |  | 
 | PressureServiceImpl::PressureServiceImpl( | 
 |     RenderFrameHost* render_frame_host, | 
 |     base::TimeDelta visible_observer_rate_limit) | 
 |     : DocumentUserData<PressureServiceImpl>(render_frame_host), | 
 |       visible_observer_rate_limit_(visible_observer_rate_limit) { | 
 |   DCHECK(render_frame_host); | 
 | } | 
 |  | 
 | PressureServiceImpl::~PressureServiceImpl() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 | } | 
 |  | 
 | void PressureServiceImpl::BindReceiver( | 
 |     mojo::PendingReceiver<blink::mojom::PressureService> receiver) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (receiver_.is_bound()) { | 
 |     mojo::ReportBadMessage("PressureService is already connected"); | 
 |     return; | 
 |   } | 
 |   receiver_.Bind(std::move(receiver)); | 
 | } | 
 |  | 
 | void PressureServiceImpl::BindObserver( | 
 |     mojo::PendingRemote<blink::mojom::PressureObserver> observer, | 
 |     BindObserverCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (!render_frame_host().IsActive() || | 
 |       render_frame_host().IsNestedWithinFencedFrame()) { | 
 |     std::move(callback).Run(blink::mojom::PressureStatus::kSecurityError); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!remote_.is_bound()) { | 
 |     auto receiver = remote_.BindNewPipeAndPassReceiver(); | 
 |     // base::Unretained is safe because Mojo guarantees the callback will not | 
 |     // be called after `remote_` is deallocated, and `remote_` is owned by | 
 |     // this class. | 
 |     remote_.set_disconnect_handler( | 
 |         base::BindRepeating(&PressureServiceImpl::OnManagerRemoteDisconnected, | 
 |                             base::Unretained(this))); | 
 |     GetDeviceService().BindPressureManager(std::move(receiver)); | 
 |   } | 
 |  | 
 |   ResetObserverState(); | 
 |  | 
 |   observer_.Bind(std::move(observer)); | 
 |   // base::Unretained is safe because Mojo guarantees the callback will not | 
 |   // be called after `observers_` is deallocated, and `observers_` is owned by | 
 |   // this class. | 
 |   observer_.set_disconnect_handler( | 
 |       base::BindRepeating(&PressureServiceImpl::OnObserverRemoteDisconnected, | 
 |                           base::Unretained(this))); | 
 |  | 
 |   client_.reset(); | 
 |   remote_->AddClient( | 
 |       client_.BindNewPipeAndPassRemote(), | 
 |       base::BindOnce(&PressureServiceImpl::DidBindObserver, | 
 |                      base::Unretained(this), std::move(callback))); | 
 |  | 
 |   // base::Unretained is safe because Mojo guarantees the callback will not | 
 |   // be called after `client_` is deallocated, and `client_` is owned by | 
 |   // this class. | 
 |   client_.set_disconnect_handler(base::BindOnce( | 
 |       &PressureServiceImpl::ResetObserverState, base::Unretained(this))); | 
 | } | 
 |  | 
 | void PressureServiceImpl::SetQuantization( | 
 |     blink::mojom::PressureQuantizationPtr quantization, | 
 |     SetQuantizationCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (!remote_.is_bound() || !client_.is_bound()) { | 
 |     receiver_.ReportBadMessage("Not connected to pressure service"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!PressureQuantizer::IsValid(*quantization)) { | 
 |     receiver_.ReportBadMessage("Invalid quantization"); | 
 |     return; | 
 |   } | 
 |  | 
 |   const bool quantization_changed = !quantizer_.IsSame(*quantization); | 
 |   if (quantization_changed) { | 
 |     ResetTimestampAndState(); | 
 |     quantizer_.Assign(std::move(quantization)); | 
 |   } | 
 |   auto status = quantization_changed | 
 |                     ? blink::mojom::SetQuantizationStatus::kChanged | 
 |                     : blink::mojom::SetQuantizationStatus::kUnchanged; | 
 |   std::move(callback).Run(status); | 
 | } | 
 |  | 
 | void PressureServiceImpl::PressureStateChanged( | 
 |     device::mojom::PressureStatePtr state, | 
 |     base::Time timestamp) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   device::mojom::PressureState quantized_state = | 
 |       quantizer_.Quantize(std::move(state)); | 
 |  | 
 |   // TODO(jsbell): Rate-limit observers in non-visible frames instead of | 
 |   //               cutting off their updates completely. | 
 |   if (timestamp - last_reported_timestamp_ < visible_observer_rate_limit_) | 
 |     return; | 
 |  | 
 |   // No need to send an update if previous value is similar. | 
 |   if (last_reported_state_ == quantized_state) | 
 |     return; | 
 |  | 
 |   if (!render_frame_host().IsActive()) { | 
 |     // TODO(jsbell): Is it safe to disconnect observers in this state? | 
 |     return; | 
 |   } | 
 |  | 
 |   if (render_frame_host().GetVisibilityState() != | 
 |       blink::mojom::PageVisibilityState::kVisible) { | 
 |     // TODO(jsbell): Rate-limit observers in non-visible frames instead of | 
 |     //               cutting off their updates completely. | 
 |     return; | 
 |   } | 
 |  | 
 |   last_reported_timestamp_ = timestamp; | 
 |   last_reported_state_ = quantized_state; | 
 |  | 
 |   observer_->OnUpdate(quantized_state.Clone()); | 
 | } | 
 |  | 
 | void PressureServiceImpl::OnObserverRemoteDisconnected() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   client_.reset(); | 
 |   ResetObserverState(); | 
 | } | 
 |  | 
 | void PressureServiceImpl::OnManagerRemoteDisconnected() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   observer_.reset(); | 
 |   client_.reset(); | 
 |   remote_.reset(); | 
 | } | 
 |  | 
 | void PressureServiceImpl::DidBindObserver(BindObserverCallback callback, | 
 |                                           bool success) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (!success) { | 
 |     std::move(callback).Run(blink::mojom::PressureStatus::kNotSupported); | 
 |     ResetObserverState(); | 
 |     return; | 
 |   } | 
 |  | 
 |   std::move(callback).Run(blink::mojom::PressureStatus::kOk); | 
 | } | 
 |  | 
 | void PressureServiceImpl::ResetObserverState() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   observer_.reset(); | 
 |   ResetTimestampAndState(); | 
 | } | 
 |  | 
 | void PressureServiceImpl::ResetTimestampAndState() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   // Makes sure that rate-limiting can't be bypassed by changing the | 
 |   // quantization scheme often. | 
 |   last_reported_timestamp_ = base::Time::Now(); | 
 |  | 
 |   // Setting to an invalid value, so any state is considered an update. | 
 |   last_reported_state_ = device::mojom::PressureState(-1); | 
 | } | 
 |  | 
 | DOCUMENT_USER_DATA_KEY_IMPL(PressureServiceImpl); | 
 |  | 
 | }  // namespace content |