| // 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/renderer_host/page_impl.h" | 
 |  | 
 | #include "base/barrier_closure.h" | 
 | #include "base/feature_list.h" | 
 | #include "base/i18n/character_encoding.h" | 
 | #include "base/trace_event/optional_trace_event.h" | 
 | #include "cc/base/features.h" | 
 | #include "cc/input/browser_controls_offset_tag_modifications.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_frame_proxy_host.h" | 
 | #include "content/browser/renderer_host/render_view_host_delegate.h" | 
 | #include "content/browser/renderer_host/render_view_host_impl.h" | 
 | #include "content/browser/shared_storage/shared_storage_features.h" | 
 | #include "content/public/browser/content_browser_client.h" | 
 | #include "content/public/browser/peak_gpu_memory_tracker_factory.h" | 
 | #include "content/public/browser/render_view_host.h" | 
 | #include "content/public/common/content_client.h" | 
 | #include "services/viz/public/mojom/compositing/offset_tag.mojom.h" | 
 | #include "third_party/blink/public/common/features.h" | 
 | #include "third_party/blink/public/common/loader/loader_constants.h" | 
 | #include "third_party/blink/public/common/shared_storage/shared_storage_utils.h" | 
 | #include "third_party/blink/public/mojom/manifest/manifest.mojom.h" | 
 | #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h" | 
 | #include "third_party/perfetto/include/perfetto/tracing/traced_value.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | PageImpl::PageImpl(RenderFrameHostImpl& rfh, PageDelegate& delegate) | 
 |     : main_document_(rfh), | 
 |       delegate_(delegate), | 
 |       text_autosizer_page_info_({0, 0, 1.f}) { | 
 |   if (base::FeatureList::IsEnabled(features::kSharedStorageSelectURLLimit)) { | 
 |     select_url_overall_budget_ = | 
 |         features::kSharedStorageSelectURLBitBudgetPerPageLoad.Get(); | 
 |     select_url_max_bits_per_site_ = | 
 |         features::kSharedStorageSelectURLBitBudgetPerSitePerPageLoad.Get(); | 
 |   } | 
 |  | 
 | #if BUILDFLAG(IS_ANDROID) | 
 |   page_proxy_ = std::make_unique<PageProxy>(this); | 
 | #endif | 
 | } | 
 |  | 
 | PageImpl::~PageImpl() { | 
 | #if BUILDFLAG(IS_ANDROID) | 
 |   page_proxy_->WillDeletePage(GetMainDocument().IsInLifecycleState( | 
 |       RenderFrameHost::LifecycleState::kPrerendering)); | 
 | #endif | 
 |  | 
 |   // 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(); | 
 |  | 
 |   // If we still have a PeakGpuMemoryTracker, then the loading it was observing | 
 |   // never completed. Cancel its callback so that we don't report partial | 
 |   // loads to UMA. | 
 |   CancelLoadingMemoryTracker(); | 
 | } | 
 |  | 
 | const std::optional<GURL>& PageImpl::GetManifestUrl() const { | 
 |   return manifest_url_; | 
 | } | 
 |  | 
 | void PageImpl::GetManifest(GetManifestCallback callback) { | 
 |   ManifestManagerHost* manifest_manager_host = | 
 |       ManifestManagerHost::GetOrCreateForPage(*this); | 
 |   manifest_manager_host->GetManifest(std::move(callback)); | 
 | } | 
 |  | 
 | bool PageImpl::IsPrimary() const { | 
 |   return main_document_->IsInPrimaryMainFrame(); | 
 | } | 
 |  | 
 | 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_); | 
 | } | 
 |  | 
 | base::WeakPtr<Page> PageImpl::GetWeakPtr() { | 
 |   return weak_factory_.GetWeakPtr(); | 
 | } | 
 |  | 
 | base::WeakPtr<PageImpl> PageImpl::GetWeakPtrImpl() { | 
 |   return weak_factory_.GetWeakPtr(); | 
 | } | 
 |  | 
 | bool PageImpl::IsPageScaleFactorOne() { | 
 |   return GetPageScaleFactor() == 1.f; | 
 | } | 
 |  | 
 | const std::string& PageImpl::GetContentsMimeType() const { | 
 |   return contents_mime_type_; | 
 | } | 
 |  | 
 | void PageImpl::SetResizableForTesting(std::optional<bool> resizable) { | 
 |   SetResizable(resizable); | 
 | } | 
 |  | 
 | void PageImpl::SetResizable(std::optional<bool> resizable) { | 
 |   resizable_ = resizable; | 
 |   delegate_->OnWebApiWindowResizableChanged(); | 
 | } | 
 |  | 
 | std::optional<bool> PageImpl::GetResizable() { | 
 |   return resizable_; | 
 | } | 
 |  | 
 | #if BUILDFLAG(IS_ANDROID) | 
 | const base::android::JavaRef<jobject>& PageImpl::GetJavaPage() { | 
 |   return page_proxy_->java_page(); | 
 | } | 
 | #endif | 
 |  | 
 | void PageImpl::OnFirstVisuallyNonEmptyPaint() { | 
 |   did_first_visually_non_empty_paint_ = true; | 
 |   delegate_->OnFirstVisuallyNonEmptyPaint(*this); | 
 | } | 
 |  | 
 | void PageImpl::OnThemeColorChanged(const std::optional<SkColor>& theme_color) { | 
 |   main_document_theme_color_ = theme_color; | 
 |   delegate_->OnThemeColorChanged(*this); | 
 | } | 
 |  | 
 | void PageImpl::DidChangeBackgroundColor(SkColor4f background_color, | 
 |                                         bool color_adjust) { | 
 |   // TODO(aaronhk): This should remain an SkColor4f | 
 |   main_document_background_color_ = background_color.toSkColor(); | 
 |   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.toSkColor()); | 
 |   } | 
 | } | 
 |  | 
 | void PageImpl::DidInferColorScheme( | 
 |     blink::mojom::PreferredColorScheme color_scheme) { | 
 |   main_document_inferred_color_scheme_ = color_scheme; | 
 |   delegate_->DidInferColorScheme(*this); | 
 | } | 
 |  | 
 | void PageImpl::NotifyPageBecameCurrent() { | 
 |   if (!IsPrimary()) { | 
 |     return; | 
 |   } | 
 |   delegate_->NotifyPageBecamePrimary(*this); | 
 | } | 
 |  | 
 | void PageImpl::SetContentsMimeType(std::string mime_type) { | 
 |   contents_mime_type_ = std::move(mime_type); | 
 | } | 
 |  | 
 | void PageImpl::OnTextAutosizerPageInfoChanged( | 
 |     blink::mojom::TextAutosizerPageInfoPtr page_info) { | 
 |   OPTIONAL_TRACE_EVENT0("content", "PageImpl::OnTextAutosizerPageInfoChanged"); | 
 |  | 
 |   // Keep a copy of `page_info` in case we create a new `blink::WebView` before | 
 |   // the next update, so that the PageImpl can tell the newly created | 
 |   // `blink::WebView` about the autosizer info. | 
 |   text_autosizer_page_info_.main_frame_width = page_info->main_frame_width; | 
 |   text_autosizer_page_info_.main_frame_layout_width = | 
 |       page_info->main_frame_layout_width; | 
 |   text_autosizer_page_info_.device_scale_adjustment = | 
 |       page_info->device_scale_adjustment; | 
 |  | 
 |   auto remote_frames_broadcast_callback = | 
 |       [this](RenderFrameProxyHost* proxy_host) { | 
 |         DCHECK(proxy_host); | 
 |         proxy_host->GetAssociatedRemoteMainFrame()->UpdateTextAutosizerPageInfo( | 
 |             text_autosizer_page_info_.Clone()); | 
 |       }; | 
 |  | 
 |   { | 
 |     TRACE_EVENT("navigation", | 
 |                 "PageImpl::OnTextAutosizerPageInfoChanged broadcast"); | 
 |     main_document_->frame_tree() | 
 |         ->root() | 
 |         ->render_manager() | 
 |         ->ExecuteRemoteFramesBroadcastMethod( | 
 |             std::move(remote_frames_broadcast_callback), | 
 |             main_document_->GetSiteInstance()->group()); | 
 |   } | 
 | } | 
 |  | 
 | void PageImpl::SetActivationStartTime(base::TimeTicks activation_start) { | 
 |   CHECK(!activation_start_time_); | 
 |   activation_start_time_ = activation_start; | 
 | } | 
 |  | 
 | void PageImpl::Activate( | 
 |     ActivationType type, | 
 |     StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts, | 
 |     std::optional<blink::ViewTransitionState> view_transition_state, | 
 |     base::OnceCallback<void(base::TimeTicks)> completion_callback) { | 
 |   TRACE_EVENT1("navigation", "PageImpl::Activate", "activation_type", type); | 
 |  | 
 |   // SetActivationStartTime() should be called first as the value is used in | 
 |   // the callback below. | 
 |   CHECK(activation_start_time_.has_value()); | 
 |  | 
 |   base::OnceClosure did_activate_render_views = base::BindOnce( | 
 |       &PageImpl::DidActivateAllRenderViewsForPrerenderingOrPreview, | 
 |       weak_factory_.GetWeakPtr(), std::move(completion_callback)); | 
 |  | 
 |   base::RepeatingClosure barrier = base::BarrierClosure( | 
 |       render_view_hosts.size(), std::move(did_activate_render_views)); | 
 |   bool view_transition_state_consumed = false; | 
 |   for (const auto& rvh : render_view_hosts) { | 
 |     auto params = blink::mojom::PrerenderPageActivationParams::New(); | 
 |  | 
 |     if (main_document_->GetRenderViewHost() == &*rvh) { | 
 |       // For prerendering activation, send activation_start only 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. | 
 |       params->activation_start = *activation_start_time_; | 
 |       // Note that there cannot be a use-after-move since the if condition | 
 |       // should be true at most once. | 
 |       CHECK(!view_transition_state_consumed); | 
 |       params->view_transition_state = std::move(view_transition_state); | 
 |       view_transition_state_consumed = true; | 
 |     } else if (type == ActivationType::kPreview) { | 
 |       // For preview activation, send activation_start to all RenderViewHosts | 
 |       // as preview loads cross-origin subframes under the capability control, | 
 |       // and activation_start time is meaningful there. | 
 |       params->activation_start = *activation_start_time_; | 
 |     } | 
 |  | 
 |     // For preview activation, there is no way to activate the previewed page | 
 |     // other than with a user action, or testing only methods. | 
 |     params->was_user_activated = | 
 |         (main_document_->frame_tree_node() | 
 |              ->has_received_user_gesture_before_nav() || | 
 |          type == ActivationType::kPreview) | 
 |             ? blink::mojom::WasActivatedOption::kYes | 
 |             : blink::mojom::WasActivatedOption::kNo; | 
 |     rvh->ActivatePrerenderedPage(std::move(params), barrier); | 
 |   } | 
 |  | 
 |   // Prepare each RenderFrameHostImpl in this Page for activation. | 
 |   main_document_->ForEachRenderFrameHostImplIncludingSpeculative( | 
 |       [](RenderFrameHostImpl* rfh) { | 
 |         rfh->RendererWillActivateForPrerenderingOrPreview(); | 
 |       }); | 
 | } | 
 |  | 
 | 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()); | 
 |   } | 
 |  | 
 |   // Dispatch PrimaryMainDocumentElementAvailable before dispatching following | 
 |   // load complete events. | 
 |   if (is_main_document_element_available()) { | 
 |     main_document_->MainDocumentElementAvailable(uses_temporary_zoom_level()); | 
 |   } | 
 |  | 
 |   main_document_->ForEachRenderFrameHostImpl( | 
 |       &RenderFrameHostImpl::MaybeDispatchDOMContentLoadedOnPrerenderActivation); | 
 |  | 
 |   if (is_on_load_completed_in_main_document()) { | 
 |     main_document_->DocumentOnLoadCompleted(); | 
 |   } | 
 |  | 
 |   if (did_first_contentful_paint_in_main_document()) { | 
 |     main_document_->OnFirstContentfulPaint(); | 
 |   } | 
 |  | 
 |   main_document_->ForEachRenderFrameHostImpl( | 
 |       &RenderFrameHostImpl::MaybeDispatchDidFinishLoadOnPrerenderActivation); | 
 | } | 
 |  | 
 | void PageImpl::DidActivateAllRenderViewsForPrerenderingOrPreview( | 
 |     base::OnceCallback<void(base::TimeTicks)> completion_callback) { | 
 |   TRACE_EVENT0("navigation", | 
 |                "PageImpl::DidActivateAllRenderViewsForPrerendering"); | 
 |  | 
 |   // Tell each RenderFrameHostImpl in this Page that activation finished. | 
 |   main_document_->ForEachRenderFrameHostImplIncludingSpeculative( | 
 |       [this](RenderFrameHostImpl* rfh) { | 
 |         if (&rfh->GetPage() != this) { | 
 |           return; | 
 |         } | 
 |         rfh->RendererDidActivateForPrerendering(); | 
 |       }); | 
 |   CHECK(activation_start_time_.has_value()); | 
 |   std::move(completion_callback).Run(*activation_start_time_); | 
 | } | 
 |  | 
 | RenderFrameHost& PageImpl::GetMainDocumentHelper() { | 
 |   return *main_document_; | 
 | } | 
 |  | 
 | RenderFrameHostImpl& PageImpl::GetMainDocument() const { | 
 |   return *main_document_; | 
 | } | 
 |  | 
 | void PageImpl::UpdateBrowserControlsState( | 
 |     cc::BrowserControlsState constraints, | 
 |     cc::BrowserControlsState current, | 
 |     bool animate, | 
 |     const std::optional<cc::BrowserControlsOffsetTagModifications>& | 
 |         offset_tag_modifications) { | 
 |   // TODO(crbug.com/40159655): Asking for the LocalMainFrame interface | 
 |   // before the RenderFrame is created is racy. | 
 |   if (!GetMainDocument().IsRenderFrameLive()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   GetMainDocument().GetRenderWidgetHost()->UpdateBrowserControlsState( | 
 |       constraints, current, animate, offset_tag_modifications); | 
 | } | 
 |  | 
 | float PageImpl::GetPageScaleFactor() const { | 
 |   return GetMainDocument().GetPageScaleFactor(); | 
 | } | 
 |  | 
 | void PageImpl::UpdateEncoding(const std::string& encoding_name) { | 
 |   if (encoding_name == last_reported_encoding_) { | 
 |     return; | 
 |   } | 
 |   last_reported_encoding_ = encoding_name; | 
 |  | 
 |   canonical_encoding_ = | 
 |       base::GetCanonicalEncodingNameByAliasName(encoding_name); | 
 | } | 
 |  | 
 | void PageImpl::NotifyVirtualKeyboardOverlayRect( | 
 |     const gfx::Rect& keyboard_rect) { | 
 |   // TODO(crbug.com/40222405): send notification to outer frames if | 
 |   // needed. | 
 |   DCHECK_EQ(virtual_keyboard_mode(), | 
 |             ui::mojom::VirtualKeyboardMode::kOverlaysContent); | 
 |   GetMainDocument().GetAssociatedLocalFrame()->NotifyVirtualKeyboardOverlayRect( | 
 |       keyboard_rect); | 
 | } | 
 |  | 
 | void PageImpl::NotifyContextMenuInsetsObservers(const gfx::Rect& safe_area) { | 
 |   GetMainDocument().GetAssociatedLocalFrame()->NotifyContextMenuInsetsObservers( | 
 |       safe_area); | 
 | } | 
 |  | 
 | void PageImpl::ShowInterestInElement(int nodeID) { | 
 |   GetMainDocument().GetAssociatedLocalFrame()->ShowInterestInElement(nodeID); | 
 | } | 
 |  | 
 | void PageImpl::SetVirtualKeyboardMode(ui::mojom::VirtualKeyboardMode mode) { | 
 |   if (virtual_keyboard_mode_ == mode) { | 
 |     return; | 
 |   } | 
 |  | 
 |   virtual_keyboard_mode_ = mode; | 
 |  | 
 |   delegate_->OnVirtualKeyboardModeChanged(*this); | 
 | } | 
 |  | 
 | base::flat_map<std::string, std::string> PageImpl::GetKeyboardLayoutMap() { | 
 |   return GetMainDocument().GetRenderWidgetHost()->GetKeyboardLayoutMap(); | 
 | } | 
 |  | 
 | int32_t PageImpl::GetSavedQueryResultIndexOrStoreCallback( | 
 |     const url::Origin& origin, | 
 |     const GURL& script_url, | 
 |     const std::string& operation_name, | 
 |     const std::u16string& query_name, | 
 |     base::OnceCallback<void(uint32_t)> callback) { | 
 |   auto key = std::make_tuple(origin, script_url, operation_name, query_name); | 
 |   auto it = select_url_saved_query_index_results_.find(key); | 
 |   if (it == select_url_saved_query_index_results_.end()) { | 
 |     select_url_saved_query_index_results_[key] = SharedStorageSavedQueryData(); | 
 |     // The result index will be determined by running the registered worklet | 
 |     // operation upon return to the SHaredStorageWorkletHost. | 
 |     return -2; | 
 |   } | 
 |   if (it->second.index == -1) { | 
 |     // The result index will be determined when a previously initiated worklet | 
 |     // operation finishes running. We save a callback that will notify us of the | 
 |     // result. | 
 |     it->second.callbacks.push(std::move(callback)); | 
 |     return -1; | 
 |   } | 
 |   // The result index has been stored from a previously resolved worklet | 
 |   // operation. | 
 |   return it->second.index; | 
 | } | 
 |  | 
 | void PageImpl::SetSavedQueryResultIndexAndRunCallbacks( | 
 |     const url::Origin& origin, | 
 |     const GURL& script_url, | 
 |     const std::string& operation_name, | 
 |     const std::u16string& query_name, | 
 |     uint32_t index) { | 
 |   auto key = std::make_tuple(origin, script_url, operation_name, query_name); | 
 |   auto it = select_url_saved_query_index_results_.find(key); | 
 |   CHECK(it != select_url_saved_query_index_results_.end()); | 
 |   CHECK_EQ(it->second.index, -1L); | 
 |   it->second.index = index; | 
 |   while (!it->second.callbacks.empty()) { | 
 |     std::move(it->second.callbacks.front()).Run(index); | 
 |     it->second.callbacks.pop(); | 
 |   } | 
 | } | 
 |  | 
 | blink::SharedStorageSelectUrlBudgetStatus | 
 | PageImpl::CheckAndMaybeDebitSelectURLBudgets(const net::SchemefulSite& site, | 
 |                                              double bits_to_charge) { | 
 |   if (!select_url_overall_budget_) { | 
 |     // The limits are not enabled. | 
 |     return blink::SharedStorageSelectUrlBudgetStatus::kSufficientBudget; | 
 |   } | 
 |  | 
 |   // Return insufficient if there is insufficient overall budget. | 
 |   if (bits_to_charge > select_url_overall_budget_.value()) { | 
 |     GetContentClient()->browser()->LogWebFeatureForCurrentPage( | 
 |         &GetMainDocument(), | 
 |         blink::mojom::WebFeature:: | 
 |             kSharedStorageAPI_SelectURLOverallPageloadBudgetInsufficient); | 
 |     return blink::SharedStorageSelectUrlBudgetStatus:: | 
 |         kInsufficientOverallPageloadBudget; | 
 |   } | 
 |  | 
 |   DCHECK(select_url_max_bits_per_site_); | 
 |  | 
 |   // Return false if the max bits per site is set to a value smaller than the | 
 |   // current bits to charge. | 
 |   if (bits_to_charge > select_url_max_bits_per_site_.value()) { | 
 |     return blink::SharedStorageSelectUrlBudgetStatus:: | 
 |         kInsufficientSitePageloadBudget; | 
 |   } | 
 |  | 
 |   // Charge the per-site budget or return insufficient if there is not enough. | 
 |   auto it = select_url_per_site_budget_.find(site); | 
 |   if (it == select_url_per_site_budget_.end()) { | 
 |     select_url_per_site_budget_[site] = | 
 |         select_url_max_bits_per_site_.value() - bits_to_charge; | 
 |   } else if (bits_to_charge > it->second) { | 
 |     // There is insufficient per-site budget remaining. | 
 |     return blink::SharedStorageSelectUrlBudgetStatus:: | 
 |         kInsufficientSitePageloadBudget; | 
 |   } else { | 
 |     it->second -= bits_to_charge; | 
 |   } | 
 |  | 
 |   // Charge the overall budget. | 
 |   select_url_overall_budget_.value() -= bits_to_charge; | 
 |   return blink::SharedStorageSelectUrlBudgetStatus::kSufficientBudget; | 
 | } | 
 |  | 
 | void PageImpl::TakeLoadingMemoryTracker(NavigationRequest* request) { | 
 |   CHECK(IsPrimary()); | 
 |   loading_memory_tracker_ = request->TakePeakGpuMemoryTracker(); | 
 | } | 
 |  | 
 | void PageImpl::ResetLoadingMemoryTracker() { | 
 |   CHECK(IsPrimary()); | 
 |   if (loading_memory_tracker_) { | 
 |     loading_memory_tracker_.reset(); | 
 |   } | 
 | } | 
 |  | 
 | void PageImpl::CancelLoadingMemoryTracker() { | 
 |   if (loading_memory_tracker_) { | 
 |     loading_memory_tracker_->Cancel(); | 
 |     loading_memory_tracker_.reset(); | 
 |   } | 
 | } | 
 |  | 
 | void PageImpl::SetLastCommitParams( | 
 |     mojom::DidCommitProvisionalLoadParamsPtr commit_params) { | 
 |   CHECK(GetMainDocument().IsOutermostMainFrame()); | 
 |   last_commit_params_ = std::move(commit_params); | 
 | } | 
 |  | 
 | mojom::DidCommitProvisionalLoadParamsPtr PageImpl::TakeLastCommitParams() { | 
 |   CHECK(GetMainDocument().IsOutermostMainFrame()); | 
 |   return std::move(last_commit_params_); | 
 | } | 
 |  | 
 | }  // namespace content |