blob: e94f434339c3ea94f7da51130a80a80392a073d8 [file] [log] [blame]
// Copyright 2023 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/display_cutout/safe_area_insets_host_impl.h"
#include "content/browser/display_cutout/display_cutout_constants.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/navigation_handle.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
namespace content {
// DocumentUserData stored inside each RenderFrameHost indicating the
// viewport-fit value for that document.
class CONTENT_EXPORT SafeAreaUserData
: public DocumentUserData<SafeAreaUserData> {
public:
~SafeAreaUserData() override = default;
void set_viewport_fit(blink::mojom::ViewportFit value) { value_ = value; }
blink::mojom::ViewportFit viewport_fit() { return value_; }
private:
explicit SafeAreaUserData(RenderFrameHost* rfh)
: DocumentUserData<SafeAreaUserData>(rfh) {}
// The viewport-fit value known by blink, or kAuto.
blink::mojom::ViewportFit value_ = blink::mojom::ViewportFit::kAuto;
// NOTE: Do not add data members without updating SetViewportFitValue.
// This is because data does not need to be stored if it consists purely
// of default values. If new data is added then SetViewportFitValue must
// check if the new data also has a default value before skipping storage.
friend DocumentUserData;
DOCUMENT_USER_DATA_KEY_DECL();
};
DOCUMENT_USER_DATA_KEY_IMPL(SafeAreaUserData);
SafeAreaInsetsHostImpl::SafeAreaInsetsHostImpl(WebContentsImpl* web_contents)
: SafeAreaInsetsHost(web_contents) {}
SafeAreaInsetsHostImpl::~SafeAreaInsetsHostImpl() = default;
void SafeAreaInsetsHostImpl::DidAcquireFullscreen(RenderFrameHost* rfh) {
fullscreen_rfh_ = static_cast<RenderFrameHostImpl*>(rfh)->GetWeakPtr();
MaybeActiveRenderFrameHostChanged();
}
void SafeAreaInsetsHostImpl::DidExitFullscreen() {
fullscreen_rfh_.reset();
MaybeActiveRenderFrameHostChanged();
}
void SafeAreaInsetsHostImpl::DidFinishNavigation(
NavigationHandle* navigation_handle) {
// If the navigation is committed and we are not a same-document
// navigation then set the current Render Frame Host and its value.
if (navigation_handle->HasCommitted() &&
!navigation_handle->IsSameDocument() &&
navigation_handle->IsInPrimaryMainFrame()) {
RenderFrameHost* rfh = navigation_handle->GetRenderFrameHost();
DCHECK(rfh);
current_rfh_ = static_cast<RenderFrameHostImpl*>(rfh)->GetWeakPtr();
MaybeActiveRenderFrameHostChanged();
}
}
void SafeAreaInsetsHostImpl::SetDisplayCutoutSafeArea(gfx::Insets insets) {
insets_ = insets;
RenderFrameHostImpl* rfh = ActiveRenderFrameHost();
if (rfh) {
SendSafeAreaToFrame(rfh, insets);
}
}
void SafeAreaInsetsHostImpl::ViewportFitChangedForFrame(
RenderFrameHost* rfh,
blink::mojom::ViewportFit value) {
DCHECK(rfh);
SetViewportFitValue(rfh, value);
// If we are the active `RenderFrameHost` frame then notify
// WebContentsObservers about the new value.
if (rfh == ActiveRenderFrameHost()) {
MaybeActiveRenderFrameHostChanged();
}
}
void SafeAreaInsetsHostImpl::MaybeActiveRenderFrameHostChanged() {
base::WeakPtr<RenderFrameHostImpl> new_active_rfh =
fullscreen_rfh_ ? fullscreen_rfh_ : current_rfh_;
if (active_rfh_.get() && new_active_rfh.get() != active_rfh_.get()) {
// Reset the SAI for the previous active frame.
SendSafeAreaToFrame(active_rfh_.get(), gfx::Insets());
}
active_rfh_ = new_active_rfh;
blink::mojom::ViewportFit new_value =
GetValueOrDefault(ActiveRenderFrameHost());
if (new_value != active_value_) {
active_value_ = new_value;
web_contents_impl_->NotifyViewportFitChanged(new_value);
}
// Update Blink so its document displays with the current insets.
SetDisplayCutoutSafeArea(insets_);
}
RenderFrameHostImpl* SafeAreaInsetsHostImpl::ActiveRenderFrameHost() {
return active_rfh_.get();
}
blink::mojom::ViewportFit SafeAreaInsetsHostImpl::GetValueOrDefault(
RenderFrameHost* rfh) const {
// The active RenderFrameHost can be null in some cases, such as if fullscreen
// mode is exited before the navigation finishes.
if (rfh) {
SafeAreaUserData* data = SafeAreaUserData::GetForCurrentDocument(rfh);
if (data) {
return data->viewport_fit();
}
}
return blink::mojom::ViewportFit::kAuto;
}
void SafeAreaInsetsHostImpl::SetViewportFitValue(
RenderFrameHost* rfh,
blink::mojom::ViewportFit value) {
if (value == blink::mojom::ViewportFit::kAuto) {
// We don't need to store UserData when it only contains the default
// value(s).
if (SafeAreaUserData::GetForCurrentDocument(rfh)) {
SafeAreaUserData::DeleteForCurrentDocument(rfh);
}
} else {
SafeAreaUserData::GetOrCreateForCurrentDocument(rfh)->set_viewport_fit(
value);
}
}
} // namespace content