blob: 1d14e843f56eca5ee1f522ad8d3785cbd222135a [file] [log] [blame]
// 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/renderer_host/page_impl.h"
#include "base/barrier_closure.h"
#include "content/browser/manifest/manifest_manager_host.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/page_delegate.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/render_view_host.h"
#include "third_party/blink/public/common/loader/loader_constants.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
namespace content {
PageImpl::PageImpl(RenderFrameHostImpl& rfh, PageDelegate& delegate)
: main_document_(rfh), delegate_(delegate) {}
PageImpl::~PageImpl() {
// As SupportsUserData is a base class of PageImpl, Page members will be
// destroyed before running ~SupportsUserData, which would delete the
// associated PageUserData objects. Avoid this by calling ClearAllUserData
// explicitly here to ensure that the PageUserData destructors can access
// associated Page object.
ClearAllUserData();
}
const absl::optional<GURL>& PageImpl::GetManifestUrl() const {
return manifest_url_;
}
void PageImpl::GetManifest(GetManifestCallback callback) {
ManifestManagerHost* manifest_manager_host =
ManifestManagerHost::GetOrCreateForCurrentDocument(&main_document_);
manifest_manager_host->GetManifest(std::move(callback));
}
bool PageImpl::IsPrimary() {
// TODO(1244137): Check for portals as well, once they are migrated to MPArch.
if (main_document_.IsFencedFrameRoot())
return false;
return main_document_.lifecycle_state() ==
RenderFrameHostImpl::LifecycleStateImpl::kActive;
}
void PageImpl::UpdateManifestUrl(const GURL& manifest_url) {
manifest_url_ = manifest_url;
// If |main_document_| is not active, the notification is sent on the page
// activation.
if (!main_document_.IsActive())
return;
main_document_.delegate()->OnManifestUrlChanged(*this);
}
void PageImpl::WriteIntoTrace(perfetto::TracedValue context) {
auto dict = std::move(context).WriteDictionary();
dict.Add("main_document", main_document_);
}
void PageImpl::OnFirstVisuallyNonEmptyPaint() {
did_first_visually_non_empty_paint_ = true;
delegate_.OnFirstVisuallyNonEmptyPaint(*this);
}
void PageImpl::OnThemeColorChanged(const absl::optional<SkColor>& theme_color) {
main_document_theme_color_ = theme_color;
delegate_.OnThemeColorChanged(*this);
}
void PageImpl::DidChangeBackgroundColor(SkColor background_color,
bool color_adjust) {
main_document_background_color_ = background_color;
delegate_.OnBackgroundColorChanged(*this);
if (color_adjust) {
// <meta name="color-scheme" content="dark"> may pass the dark canvas
// background before the first paint in order to avoid flashing the white
// background in between loading documents. If we perform a navigation
// within the same renderer process, we keep the content background from the
// previous page while rendering is blocked in the new page, but for cross
// process navigations we would paint the default background (typically
// white) while the rendering is blocked.
main_document_.GetRenderWidgetHost()->GetView()->SetContentBackgroundColor(
background_color);
}
}
void PageImpl::SetContentsMimeType(std::string mime_type) {
contents_mime_type_ = std::move(mime_type);
}
void PageImpl::SetActivationStartTime(base::TimeTicks activation_start) {
DCHECK(!activation_start_time_for_prerendering_);
activation_start_time_for_prerendering_ = activation_start;
}
void PageImpl::ActivateForPrerendering(
std::set<RenderViewHostImpl*>& render_view_hosts) {
base::OnceClosure did_activate_render_views =
base::BindOnce(&PageImpl::DidActivateAllRenderViewsForPrerendering,
weak_factory_.GetWeakPtr());
base::RepeatingClosure barrier = base::BarrierClosure(
render_view_hosts.size(), std::move(did_activate_render_views));
for (RenderViewHostImpl* rvh : render_view_hosts) {
base::TimeTicks navigation_start_to_send;
// Only send navigation_start to the RenderViewHost for the main frame to
// avoid sending the info cross-origin. Only this RenderViewHost needs the
// info, as we expect the other RenderViewHosts are made for cross-origin
// iframes which have not yet loaded their document. To the renderer, it
// just looks like an ongoing navigation is happening in the frame and has
// not yet committed. These RenderViews still need to know about activation
// so their documents are created in the non-prerendered state once their
// navigation is committed.
if (main_document_.GetRenderViewHost() == rvh)
navigation_start_to_send = *activation_start_time_for_prerendering_;
rvh->ActivatePrerenderedPage(navigation_start_to_send, barrier);
}
// Prepare each RenderFrameHostImpl in this Page for activation.
// TODO(https://crbug.com/1232528): Currently we check GetPage() below because
// RenderFrameHostImpls may be in a different Page, if, e.g., they are in an
// inner WebContents. These are in a different FrameTree which might not know
// it is being prerendered. We should teach these FrameTrees that they are
// being prerendered, or ban inner FrameTrees in a prerendering page.
main_document_.ForEachRenderFrameHost(base::BindRepeating(
[](PageImpl* page, RenderFrameHostImpl* rfh) {
if (&rfh->GetPage() != page)
return;
rfh->RendererWillActivateForPrerendering();
},
this));
}
void PageImpl::MaybeDispatchLoadEventsOnPrerenderActivation() {
DCHECK(IsPrimary());
// Dispatch LoadProgressChanged notification on activation with the
// prerender last load progress value if the value is not equal to
// blink::kFinalLoadProgress, whose notification is dispatched during call
// to DidStopLoading.
if (load_progress() != blink::kFinalLoadProgress)
main_document_.DidChangeLoadProgress(load_progress());
main_document_.ForEachRenderFrameHost(
base::BindRepeating([](RenderFrameHostImpl* rfh) {
rfh->MaybeDispatchDOMContentLoadedOnPrerenderActivation();
}));
if (is_on_load_completed_in_main_document())
main_document_.DocumentOnLoadCompleted();
main_document_.ForEachRenderFrameHost(
base::BindRepeating([](RenderFrameHostImpl* rfh) {
rfh->MaybeDispatchDidFinishLoadOnPrerenderActivation();
}));
}
void PageImpl::DidActivateAllRenderViewsForPrerendering() {
// Tell each RenderFrameHostImpl in this Page that activation finished.
main_document_.ForEachRenderFrameHost(base::BindRepeating(
[](PageImpl* page, RenderFrameHostImpl* rfh) {
if (&rfh->GetPage() != page)
return;
rfh->RendererDidActivateForPrerendering();
},
this));
}
RenderFrameHost& PageImpl::GetMainDocumentHelper() {
return main_document_;
}
RenderFrameHostImpl& PageImpl::GetMainDocument() const {
return main_document_;
}
} // namespace content