void NavigationRequest::DidCommitNavigation( const...

Criado em: 12 de janeiro de 2026

Respondido usando GPT-5.2 Thinking por Chat01

Pergunta

void NavigationRequest::DidCommitNavigation(
const mojom::DidCommitProvisionalLoadParams& params,
bool navigation_entry_committed,
bool did_replace_entry,
const GURL& previous_main_frame_url) {
TRACE_EVENT("navigation", "NavigationRequest::DidCommitNavigation",
perfetto::Flow::FromPointer(this));
common_params_->url = params.url;
did_replace_entry_ = did_replace_entry;
should_update_history_ = params.should_update_history;
navigation_handle_timing_.navigation_commit_received_time =
params.commit_navigation_start;
navigation_handle_timing_.navigation_commit_reply_sent_time =
params.commit_reply_sent;
navigation_handle_timing_.navigation_did_commit_time = base::TimeTicks::Now();
// A same document navigation with the same url, and no user-gesture is
// typically the result of 'history.replaceState().' As the page is
// controlling this, the user doesn't really think of this as a navigation
// and it doesn't make sense to log this in history. Logging this in history
// would lead to lots of visits to a particular page, which impacts the
// visit count.
// Navigations in non-primary frame trees don't appear in history.
if ((should_update_history_ && IsSameDocument() && !HasUserGesture() &&
params.url == previous_main_frame_url) ||
!GetRenderFrameHost()->GetPage().IsPrimary()) {
should_update_history_ = false;
}
previous_main_frame_url_ = previous_main_frame_url;
// It should be kept in sync with the check in
// RenderFrameHostImpl::TakeNewDocumentPropertiesFromNavigation.
if (DidEncounterError()) {
EnterChildTraceEvent("DidCommitNavigation: error page", this);
SetState(DID_COMMIT_ERROR_PAGE);
} else {
EnterChildTraceEvent("DidCommitNavigation", this);
SetState(DID_COMMIT);
}
navigation_or_document_handle_->OnNavigationCommitted(this);
// If the navigation committed successfully, pass ownership of ViewTransition
// resources to the new view. This ensures that the resources are cleaned up
// if the new renderer process terminates before taking ownership of them.
if (view_transition_resources_ && state_ == DID_COMMIT) {
GetRenderFrameHost()
->GetRenderWidgetHost()
->GetRenderWidgetHostViewBase()
->SetViewTransitionResources(std::move(view_transition_resources_));
}
StopCommitTimeout();
// Switching BrowsingInstance because of COOP or top-level cross browsing
// instance navigation resets the name of the frame. The renderer already
// knows locally about it because we sent an empty name at frame creation
// time. The renderer has now committed the page and we can safely enforce the
// empty name on the browser side.
BrowserContext context =
frame_tree_node_->navigator().controller().GetBrowserContext();
bool should_clear_browsing_instance_name =
browsing_context_group_swap().ShouldClearWindowName() ||
(commit_params().is_cross_site_cross_browsing_context_group &&
base::FeatureList::IsEnabled(
features::kClearCrossSiteCrossBrowsingContextGroupWindowName) &&
GetContentClient()
->browser()
->IsClearWindowNameForNewBrowsingContextGroupAllowed(context));
if (should_clear_browsing_instance_name) {
std::string name, unique_name;
// The "swap" only affect main frames, that have an empty unique name.
if (features::GetBrowsingContextMode() ==
features::BrowsingContextStateImplementationType::
kLegacyOneToOneWithFrameTreeNode) {
DCHECK(frame_tree_node_->unique_name().empty());
GetRenderFrameHost()->browsing_context_state()->SetFrameName(name,
unique_name);
}
}
// Record metrics for the time it took to commit the navigation if it was to
// another document without error.
if (!IsSameDocument() && state_ != DID_COMMIT_ERROR_PAGE) {
ui::PageTransition transition =
ui::PageTransitionFromInt(common_params_->transition);
base::Process::Priority priority =
GetRenderFrameHost()->GetProcess()->GetPriority();
text
RecordStartToCommitMetrics(
common_params_->navigation_start, transition, ready_to_commit_time_,
priority, is_same_process_, frame_tree_node_->IsMainFrame());
}
DCHECK(!frame_tree_node_->IsMainFrame() || navigation_entry_committed)
<< "Only subframe navigations can get here without changing the "
<< "NavigationEntry";
subframe_entry_committed_ = navigation_entry_committed;
// For successful navigations, ensure the frame owner element is no longer
// collapsed as a result of a prior navigation.
if (state_ != DID_COMMIT_ERROR_PAGE &&
(!frame_tree_node()->IsMainFrame() ||
frame_tree_node()->IsFencedFrameRoot())) {
// The last committed load in collapsed frames will be an error page with
// |kUnreachableWebDataURL|. Same-document navigation should not be
// possible.
DCHECK(!IsSameDocument() || !frame_tree_node()->is_collapsed());
frame_tree_node()->SetCollapsed(false);
}
if (service_worker_handle_ &&
service_worker_handle_->service_worker_client()) {
// Notify the service worker navigation handle that the navigation finished
// committing.
service_worker_handle_->service_worker_client()->OnEndNavigationCommit();
}
// TODO(crbug.com/40249865): consider using NavigationOrDocumentHandle
// instead once we can get a WeakDocumentPtr from NavigationOrDocumentHandle.
if (subresource_proxying_url_loader_service_bind_context_) {
DCHECK(!IsSameDocument());
text
subresource_proxying_url_loader_service_bind_context_
->OnDidCommitNavigation(GetRenderFrameHost()->GetWeakDocumentPtr());
}
if (keep_alive_url_loader_factory_context_) {
DCHECK(!IsSameDocument());
text
keep_alive_url_loader_factory_context_->OnDidCommitNavigation(this);
}
if (fetch_later_loader_factory_context_) {
DCHECK(!IsSameDocument());
text
fetch_later_loader_factory_context_->OnDidCommitNavigation(this);
}
// Network status of the entire frame tree needs to be updated once a
// NavigationRequest commits. When fenced frames revoke network access by
// calling window.fence.disableUntrustedNetwork, the returned promise cannot
// be resolved until ongoing navigations in descendant frames complete.
GetRenderFrameHost()
->GetOutermostMainFrame()
->CalculateUntrustedNetworkStatus();
if (!pending_commit_metrics_.start_time.is_null()) {
const bool is_for_mhtml = IsMhtmlMimeType(GetMimeType());
base::UmaHistogramTimes(
is_for_mhtml ? "Navigation.PendingCommit.Duration.MHTML"
: "Navigation.PendingCommit.Duration.Regular",
base::TimeTicks::Now() - pending_commit_metrics_.start_time);
const bool did_block_get_frame_host_for_navigation =
pending_commit_metrics_.blocked_count > 0;
base::UmaHistogramBoolean(
is_for_mhtml
? "Navigation.PendingCommit.DidBlockGetFrameHostForNavigation.MHTML"
: "Navigation.PendingCommit.DidBlockGetFrameHostForNavigation."
"Regular",
did_block_get_frame_host_for_navigation);
if (did_block_get_frame_host_for_navigation) {
base::UmaHistogramCounts100(
is_for_mhtml ? "Navigation.PendingCommit.BlockedCount.MHTML"
: "Navigation.PendingCommit.BlockedCount.Regular",
pending_commit_metrics_.blocked_count);
base::UmaHistogramCounts100(
is_for_mhtml ? "Navigation.PendingCommit.BlockedCommitCount.MHTML"
: "Navigation.PendingCommit.BlockedCommitCount.Regular",
pending_commit_metrics_.blocked_commit_count);
}
}
// DO NOT ADD CODE after this.
// UnblockPendingSubframeNavigationRequestsIfNeeded() resumes throttles, which
// may cause the destruction of this NavigationRequest.
UnblockPendingSubframeNavigationRequestsIfNeeded();
}
SiteInfo NavigationRequest::GetSiteInfoForCommonParamsURL() {
UrlInfo url_info = GetUrlInfo();
// TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
// be correct for cross-BrowsingInstance redirects.
return SiteInfo::Create(starting_site_instance_->GetIsolationContext(),
url_info);
}
// TODO(zetamoo): Try to merge this function inside its callers.
void NavigationRequest::UpdateStateFollowingRedirect(
const GURL& new_referrer_url) {
// The navigation should not redirect to a "renderer debug" url. It should be
// blocked in NavigationRequest::OnRequestRedirected or in
// ResourceLoader::OnReceivedRedirect.
// Note: the |common_params_->url| below is the post-redirect URL.
// See https://crbug.com/728398.
CHECK(!blink::IsRendererDebugURL(common_params_->url));
// Re-generate the feature context to ensure that the runtime-enabled features
// have the correct state values.
runtime_feature_state_context_ = blink::RuntimeFeatureStateContext();
// Update the navigation parameters.
if (!(common_params_->transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT)) {
sanitized_referrer_->url = new_referrer_url;
sanitized_referrer_ =
Referrer::SanitizeForRequest(common_params_->url, sanitized_referrer_);
}
common_params_->referrer = sanitized_referrer_.Clone();
was_redirected_ = true;
redirect_chain_.push_back(common_params_->url);
SetState(WILL_REDIRECT_REQUEST);
processing_navigation_throttle_ = true;
#if BUILDFLAG(IS_ANDROID)
navigation_handle_proxy_->DidRedirect();
#endif
}
herevoid NavigationRequest::DidCommitNavigation(
const mojom::DidCommitProvisionalLoadParams& params,
bool navigation_entry_committed,
bool did_replace_entry,
const GURL& previous_main_frame_url) {
TRACE_EVENT("navigation", "NavigationRequest::DidCommitNavigation",
perfetto::Flow::FromPointer(this));
common_params_->url = params.url;
did_replace_entry_ = did_replace_entry;
should_update_history_ = params.should_update_history;
navigation_handle_timing_.navigation_commit_received_time =
params.commit_navigation_start;
navigation_handle_timing_.navigation_commit_reply_sent_time =
params.commit_reply_sent;
navigation_handle_timing_.navigation_did_commit_time = base::TimeTicks::Now();
// A same document navigation with the same url, and no user-gesture is
// typically the result of 'history.replaceState().' As the page is
// controlling this, the user doesn't really think of this as a navigation
// and it doesn't make sense to log this in history. Logging this in history
// would lead to lots of visits to a particular page, which impacts the
// visit count.
// Navigations in non-primary frame trees don't appear in history.
if ((should_update_history_ && IsSameDocument() && !HasUserGesture() &&
params.url == previous_main_frame_url) ||
!GetRenderFrameHost()->GetPage().IsPrimary()) {
should_update_history_ = false;
}
previous_main_frame_url_ = previous_main_frame_url;
// It should be kept in sync with the check in
// RenderFrameHostImpl::TakeNewDocumentPropertiesFromNavigation.
if (DidEncounterError()) {
EnterChildTraceEvent("DidCommitNavigation: error page", this);
SetState(DID_COMMIT_ERROR_PAGE);
} else {
EnterChildTraceEvent("DidCommitNavigation", this);
SetState(DID_COMMIT);
}
navigation_or_document_handle_->OnNavigationCommitted(this);
// If the navigation committed successfully, pass ownership of ViewTransition
// resources to the new view. This ensures that the resources are cleaned up
// if the new renderer process terminates before taking ownership of them.
if (view_transition_resources_ && state_ == DID_COMMIT) {
GetRenderFrameHost()
->GetRenderWidgetHost()
->GetRenderWidgetHostViewBase()
->SetViewTransitionResources(std::move(view_transition_resources_));
}
StopCommitTimeout();
// Switching BrowsingInstance because of COOP or top-level cross browsing
// instance navigation resets the name of the frame. The renderer already
// knows locally about it because we sent an empty name at frame creation
// time. The renderer has now committed the page and we can safely enforce the
// empty name on the browser side.
BrowserContext context =
frame_tree_node_->navigator().controller().GetBrowserContext();
bool should_clear_browsing_instance_name =
browsing_context_group_swap().ShouldClearWindowName() ||
(commit_params().is_cross_site_cross_browsing_context_group &&
base::FeatureList::IsEnabled(
features::kClearCrossSiteCrossBrowsingContextGroupWindowName) &&
GetContentClient()
->browser()
->IsClearWindowNameForNewBrowsingContextGroupAllowed(context));
if (should_clear_browsing_instance_name) {
std::string name, unique_name;
// The "swap" only affect main frames, that have an empty unique name.
if (features::GetBrowsingContextMode() ==
features::BrowsingContextStateImplementationType::
kLegacyOneToOneWithFrameTreeNode) {
DCHECK(frame_tree_node_->unique_name().empty());
GetRenderFrameHost()->browsing_context_state()->SetFrameName(name,
unique_name);
}
}
// Record metrics for the time it took to commit the navigation if it was to
// another document without error.
if (!IsSameDocument() && state_ != DID_COMMIT_ERROR_PAGE) {
ui::PageTransition transition =
ui::PageTransitionFromInt(common_params_->transition);
base::Process::Priority priority =
GetRenderFrameHost()->GetProcess()->GetPriority();
text
RecordStartToCommitMetrics(
common_params_->navigation_start, transition, ready_to_commit_time_,
priority, is_same_process_, frame_tree_node_->IsMainFrame());
}
DCHECK(!frame_tree_node_->IsMainFrame() || navigation_entry_committed)
<< "Only subframe navigations can get here without changing the "
<< "NavigationEntry";
subframe_entry_committed_ = navigation_entry_committed;
// For successful navigations, ensure the frame owner element is no longer
// collapsed as a result of a prior navigation.
if (state_ != DID_COMMIT_ERROR_PAGE &&
(!frame_tree_node()->IsMainFrame() ||
frame_tree_node()->IsFencedFrameRoot())) {
// The last committed load in collapsed frames will be an error page with
// |kUnreachableWebDataURL|. Same-document navigation should not be
// possible.
DCHECK(!IsSameDocument() || !frame_tree_node()->is_collapsed());
frame_tree_node()->SetCollapsed(false);
}
if (service_worker_handle_ &&
service_worker_handle_->service_worker_client()) {
// Notify the service worker navigation handle that the navigation finished
// committing.
service_worker_handle_->service_worker_client()->OnEndNavigationCommit();
}
// TODO(crbug.com/40249865): consider using NavigationOrDocumentHandle
// instead once we can get a WeakDocumentPtr from NavigationOrDocumentHandle.
if (subresource_proxying_url_loader_service_bind_context_) {
DCHECK(!IsSameDocument());
text
subresource_proxying_url_loader_service_bind_context_
->OnDidCommitNavigation(GetRenderFrameHost()->GetWeakDocumentPtr());
}
if (keep_alive_url_loader_factory_context_) {
DCHECK(!IsSameDocument());
text
keep_alive_url_loader_factory_context_->OnDidCommitNavigation(this);
}
if (fetch_later_loader_factory_context_) {
DCHECK(!IsSameDocument());
text
fetch_later_loader_factory_context_->OnDidCommitNavigation(this);
}
// Network status of the entire frame tree needs to be updated once a
// NavigationRequest commits. When fenced frames revoke network access by
// calling window.fence.disableUntrustedNetwork, the returned promise cannot
// be resolved until ongoing navigations in descendant frames complete.
GetRenderFrameHost()
->GetOutermostMainFrame()
->CalculateUntrustedNetworkStatus();
if (!pending_commit_metrics_.start_time.is_null()) {
const bool is_for_mhtml = IsMhtmlMimeType(GetMimeType());
base::UmaHistogramTimes(
is_for_mhtml ? "Navigation.PendingCommit.Duration.MHTML"
: "Navigation.PendingCommit.Duration.Regular",
base::TimeTicks::Now() - pending_commit_metrics_.start_time);
const bool did_block_get_frame_host_for_navigation =
pending_commit_metrics_.blocked_count > 0;
base::UmaHistogramBoolean(
is_for_mhtml
? "Navigation.PendingCommit.DidBlockGetFrameHostForNavigation.MHTML"
: "Navigation.PendingCommit.DidBlockGetFrameHostForNavigation."
"Regular",
did_block_get_frame_host_for_navigation);
if (did_block_get_frame_host_for_navigation) {
base::UmaHistogramCounts100(
is_for_mhtml ? "Navigation.PendingCommit.BlockedCount.MHTML"
: "Navigation.PendingCommit.BlockedCount.Regular",
pending_commit_metrics_.blocked_count);
base::UmaHistogramCounts100(
is_for_mhtml ? "Navigation.PendingCommit.BlockedCommitCount.MHTML"
: "Navigation.PendingCommit.BlockedCommitCount.Regular",
pending_commit_metrics_.blocked_commit_count);
}
}
// DO NOT ADD CODE after this.
// UnblockPendingSubframeNavigationRequestsIfNeeded() resumes throttles, which
// may cause the destruction of this NavigationRequest.
UnblockPendingSubframeNavigationRequestsIfNeeded();
}
SiteInfo NavigationRequest::GetSiteInfoForCommonParamsURL() {
UrlInfo url_info = GetUrlInfo();
// TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
// be correct for cross-BrowsingInstance redirects.
return SiteInfo::Create(starting_site_instance_->GetIsolationContext(),
url_info);
}
// TODO(zetamoo): Try to merge this function inside its callers.
void NavigationRequest::UpdateStateFollowingRedirect(
const GURL& new_referrer_url) {
// The navigation should not redirect to a "renderer debug" url. It should be
// blocked in NavigationRequest::OnRequestRedirected or in
// ResourceLoader::OnReceivedRedirect.
// Note: the |common_params_->url| below is the post-redirect URL.
// See https://crbug.com/728398.
CHECK(!blink::IsRendererDebugURL(common_params_->url));
// Re-generate the feature context to ensure that the runtime-enabled features
// have the correct state values.
runtime_feature_state_context_ = blink::RuntimeFeatureStateContext();
// Update the navigation parameters.
if (!(common_params_->transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT)) {
sanitized_referrer_->url = new_referrer_url;
sanitized_referrer_ =
Referrer::SanitizeForRequest(common_params_->url, sanitized_referrer_);
}
common_params_->referrer = sanitized_referrer_.Clone();
was_redirected_ = true;
redirect_chain_.push_back(common_params_->url);
SetState(WILL_REDIRECT_REQUEST);
processing_navigation_throttle_ = true;
#if BUILDFLAG(IS_ANDROID)
navigation_handle_proxy_->DidRedirect();
#endif
}
here and
void TestRenderFrameHost::SendBeforeUnload(
bool is_reload,
base::WeakPtr<RenderFrameHostImpl> impl,
bool for_legacy,
const bool is_renderer_initiated_navigation) {
if (on_sendbeforeunload_begin_) {
std::move(on_sendbeforeunload_begin_).Run();
}
RenderFrameHostImpl::SendBeforeUnload(is_reload, std::move(impl), for_legacy,
is_renderer_initiated_navigation);
if (on_sendbeforeunload_end_) {
std::move(on_sendbeforeunload_end_).Run();
}
}
mojom::DidCommitProvisionalLoadParamsPtr
TestRenderFrameHost::BuildDidCommitParams(bool did_create_new_entry,
const GURL& url,
ui::PageTransition transition,
int response_code,
bool is_same_document) {
auto params = mojom::DidCommitProvisionalLoadParams::New();
params->url = url;
params->referrer = blink::mojom::Referrer::New();
params->transition = transition;
params->should_update_history = true;
params->did_create_new_entry = did_create_new_entry;
params->contents_mime_type = "text/html";
params->method = "GET";
params->http_status_code = response_code;
params->history_list_was_cleared = simulate_history_list_was_cleared_;
params->post_id = -1;
// Simulate Blink assigning an item and document sequence number to the
// navigation.
params->item_sequence_number =
(base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
params->document_sequence_number = params->item_sequence_number + 1;
// When the user hits enter in the Omnibox without changing the URL, Blink
// behaves similarly to a reload and does not change the item and document
// sequence numbers. Simulate this behavior here too.
if (PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
NavigationEntryImpl entry =
frame_tree_node()->navigator().controller().GetLastCommittedEntry();
if (entry && entry->GetURL() == url) {
FrameNavigationEntry frame_entry =
entry->GetFrameEntry(frame_tree_node());
if (frame_entry) {
params->item_sequence_number = frame_entry->item_sequence_number();
params->document_sequence_number =
frame_entry->document_sequence_number();
}
}
}
// In most cases, the origin will match the URL's origin. Tests that need to
// check corner cases (like about:blank) should specify the origin and
// initiator_base_url params manually.
url::Origin origin = url::Origin::Create(url);
params->origin = origin;
params->page_state = blink::PageState::CreateForTestingWithSequenceNumbers(
url, params->item_sequence_number, params->document_sequence_number);
params->commit_navigation_start = base::TimeTicks::Now();
params->commit_navigation_end = base::TimeTicks::Now();
return params;
}
and
mojom::DidCommitProvisionalLoadParamsPtr
NavigationSimulatorImpl::BuildDidCommitProvisionalLoadParams(
bool same_document,
bool failed_navigation,
int last_http_status_code) {
auto params = mojom::DidCommitProvisionalLoadParams::New();
params->url = navigation_url_;
params->referrer = mojo::Clone(referrer_);
params->contents_mime_type = contents_mime_type_;
params->history_list_was_cleared = history_list_was_cleared_;
RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
params->did_create_new_entry = DidCreateNewEntry(
same_document,
should_replace_current_entry_ ||
(request_ && request_->common_params().should_replace_current_entry));
// See CalculateTransition() in render_frame_host_impl.cc.
if (frame_tree_node_->IsMainFrame() && request_) {
params->transition =
ui::PageTransitionFromInt(request_->common_params().transition);
} else if (!params->did_create_new_entry &&
PageTransitionCoreTypeIs(transition_,
ui::PAGE_TRANSITION_MANUAL_SUBFRAME)) {
// Non-standard commits (replacements, reloads, history navigations, etc) on
// subframe will result in PAGE_TRANSITION_AUTO_SUBFRAME transition. These
// navigations can be detected from the |did_create_new_entry| value (on
// subframes).
params->transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
} else {
params->transition = transition_;
}
params->navigation_token = request_
? request_->commit_params().navigation_token
: base::UnguessableToken::Create();
params->post_id = post_id_;
params->method = request_ ? request_->common_params().method : "GET";
if (failed_navigation) {
params->url_is_unreachable = true;
params->should_update_history = false;
} else if (same_document) {
params->should_update_history = true;
} else {
// TODO(crbug.com/40161149): Reconsider how we calculate
// should_update_history.
bool are_404_navigations_saved_in_history =
base::FeatureList::IsEnabled(history::kVisitedLinksOn404);
params->should_update_history = are_404_navigations_saved_in_history ||
response_headers_->response_code() != 404;
}
// This mirrors the calculation in
// RenderFrameImpl::MakeDidCommitProvisionalLoadParams.
// TODO(wjmaclean): If params->url is about:blank or about:srcdoc then we
// should also populate params->initiator_base_url in a manner similar to
// RenderFrameImpl::MakeDidCommitProvisionalLoadParams.
if (same_document) {
params->origin = current_rfh->GetLastCommittedOrigin();
} else {
params->origin =
origin_.value_or(request_->commit_params().origin_to_commit);
}
if (same_document) {
// Same document navigations always retain the last HTTP status code.
params->http_status_code = last_http_status_code;
} else if (request_ && request_->commit_params().http_response_code != -1) {
// If we have a valid HTTP response code in |request_|, use it.
params->http_status_code = request_->commit_params().http_response_code;
} else {
// Otherwise, unit tests will never issue real network requests and thus
// will never receive any HTTP response.
params->http_status_code = 0;
}
CHECK(same_document || request_);
params->permissions_policy_header = std::move(permissions_policy_header_);
// Simulate Blink assigning a item sequence number and document sequence
// number to the navigation.
params->item_sequence_number = ++g_unique_identifier;
if (same_document) {
FrameNavigationEntry* current_entry =
web_contents_->GetController().GetLastCommittedEntry()->GetFrameEntry(
frame_tree_node_);
params->document_sequence_number =
current_entry->document_sequence_number();
} else {
params->document_sequence_number = ++g_unique_identifier;
}
// Simulate embedding token creation.
if (!same_document)
params->embedding_token = base::UnguessableToken::Create();
params->page_state = page_state_.value_or(
blink::PageState::CreateForTestingWithSequenceNumbers(
navigation_url_, params->item_sequence_number,
params->document_sequence_number));
params->is_overriding_user_agent =
request_ ? (request_->commit_params().is_overriding_user_agent &&
frame_tree_node_->IsMainFrame())
: false;
params->insecure_request_policy = insecure_request_policy_;
params->insecure_navigations_set = insecure_navigations_set_;
params->has_potentially_trustworthy_unique_origin =
has_potentially_trustworthy_unique_origin_;
params->commit_navigation_start = base::TimeTicks::Now();
params->commit_navigation_end = base::TimeTicks::Now();
return params;
}

suppose i went from gmailinbox to drafts and the url changed, what happens w regards to persisitece in chrome db, i have tested it, and whenever i test it, all of these navigations are logged accurately

Pensamento

Addressing Chrome's history persistence and navigation commit

The user is asking about Chrome's page navigation behavior, where same-document navigation might not be logged in history. When navigating, Chrome chooses to update the history based on conditions like user gestures and URL changes. It typically records actions for the main frame, not subframes, and only if should_update_history is enabled. The behavior differs between full document navigation and internal single-page app (SPA) transitions.

Explaining same-document navigation and history updates

When navigating within the same document (e.g., URL changes but no page reload), Chrome may not update history unless there’s a user gesture or a URL change. In the case of SPAs, like Gmail, where clicking between inbox and drafts triggers a pushState with a URL change, history is logged due to the gesture. The history is saved in Chrome's "History" DB and includes specific tables for visits and URLs.

Explaining Chrome's history persistence and session navigation

Chrome creates a new navigation entry when necessary, controlled by ShouldUpdateHistory. For same-document navigation, like a pushState with a URL change, the history entry will be created. Persistence happens when the navigation commits in the HistoryService, saving to a session file or History DB. Gmail’s SPA navigation updates the browser history when URL changes, even without gestures. Special handling for 404 pages plays a role here too.

Explaining history persistence in navigating SPAs

There are three layers to how Chrome manages navigation. First, the renderer commits navigation and sends update parameters; second, the browser updates session history; third, the HistoryService records the visit in the database if conditions are met. For same-document SPAs, history is recorded if the URL changes via pushState or if there’s a user gesture, but replaceState does not create a new entry.

Explaining persistence and logging behavior for Gmail navigation

Chrome stores each unique URL in the History DB with visit details like time and referrer. For Gmail, URL changes like /inbox vs. /drafts create separate history entries. Even with same-document navigation, like hash changes (#inbox), Chrome treats it as a new entry, logging details unless overridden by conditions like Incognito mode or disabled history. In tests, should_update_history is always true, so visits get logged automatically.

Explaining Gmail's navigation history behavior

When Gmail switches routes, it triggers a same-document navigation. If the user clicks “Drafts” and the URL changes, Chrome records the navigation and updates HistoryDB. This involves pushState or sometimes replaceState. If should_update_history is true, the history entry gets created. The main frame's navigation, not subframes, is logged to HistoryService, while visited link data is handled separately.

Explaining Gmail's navigation and history persistence

When you navigate from inbox to drafts in Gmail, Chrome treats it as a same-document navigation. It updates session history and can add a new entry or replace the current one. If the URL changes and should_update_history is true, the visit is logged in the History DB. Without a user gesture or with certain conditions, such changes might not be recorded.

Thought for 49s

Resposta

If you click Gmail Inbox → Drafts and the visible URL changes, Chrome treats that as a main-frame same-document navigation in most Gmail builds (SPA routing via history.pushState() or a #hash change). That has two different “persistence” tracks in Chrome:

1) Session history / tab state (what Back/Forward uses)

On commit, NavigationRequest::DidCommitNavigation() updates the NavigationEntry state (session history). Whether it creates a new entry or replaces the current one comes from the renderer’s commit params:

  • params->did_create_new_entry → new entry (typical pushState / user click)
  • did_replace_entry / should_replace_current_entry → replace entry (typical replaceState)

So for Inbox → Drafts (user click, URL changes), you usually get a new session-history entry, and Back/Forward works the way you observed.

2) “Chrome DB” history (History SQLite: urls/visits)

Whether it gets written as a history visit depends primarily on params.should_update_history and a couple of browser-side guards.

The important guard in your pasted code is this one:

cpp
if ((should_update_history_ && IsSameDocument() && !HasUserGesture() && params.url == previous_main_frame_url) || !GetRenderFrameHost()->GetPage().IsPrimary()) { should_update_history_ = false; }

That means Chrome will turn off history logging only for the special case:

  • same-document
  • same URL as before
  • no user gesture
  • (and it must be in the primary page/frame tree)

That’s aimed at the “silent” case (typically history.replaceState() with no user action) where logging would spam history.

Why Inbox → Drafts does get logged in your tests

For Inbox → Drafts:

  • It’s same-document, yes.
  • But the URL changed, so params.url == previous_main_frame_url is false.
  • And the click is a user gesture anyway.

So Chrome keeps should_update_history_ true (assuming the renderer set it true, which Gmail typically does for user-initiated route changes), and later the history backend records a visit. That matches what you saw: “all of these navigations are logged accurately.”

How your test helpers bias this

In the snippets you posted:

  • TestRenderFrameHost::BuildDidCommitParams() sets params->should_update_history = true; unconditionally.
  • NavigationSimulatorImpl::BuildDidCommitProvisionalLoadParams() also sets should_update_history to true for same_document (unless it’s a failed nav, etc.).

So unless you explicitly simulate the exact suppression case (same-doc + same URL + no gesture + primary), your tests will almost always record these navigations.

Practical takeaway

For Gmail-style SPA navigations where URL changes (hash or path) and the user clicked something:

  • Session history entry: yes (so Back/Forward works)
  • History DB visit: usually yes, because the “don’t log replaceState spam” check does not apply

If you want to reproduce the “not persisted / not logged” behavior, simulate something like:

  • same-document commit
  • params.url == previous_main_frame_url
  • HasUserGesture() == false
  • primary page

That’s the case Chrome intentionally suppresses.

Pergunta

so if i went from gmailinbox to drafts and the url changed, this would show up im chrome's spqlite3 browser history db?

Pensamento

Exploring history update behavior in browsers

When should a browser create a history visit? It happens if should_update_history is set to true and the URL changes. Suppression happens for same URLs without user interaction. Site navigation methods like replaceState can modify these behaviors, but generally URL changes process as history entries.

Considering pushState and fragment storage behavior

When a pushState occurs (like from a click), the history should be updated with the new URL. Chrome typically stores the full URL, including fragments, but there might be normalization for username/password details. I’m not entirely sure about Gmail's URL structure with fragments, though—whether those get recorded in history.

Exploring Gmail URL handling in Chrome history

Chrome history typically stores full URLs, including fragments, but visited links might ignore fragments for privacy. When there's a fragment change, it may be treated as a same-document update instead of creating a new entry unless a user gesture triggers it. The history database uses file paths and tables to store this info.

Clarifying history entry for Gmail and Chrome behavior

In Chrome history, a new visit is logged or the visit count is incremented. Gmail sometimes

Thought for 20s

Resposta

Most likely yesInbox → Drafts with a URL change (e.g., …/#inbox…/#drafts) will usually produce a row in Chrome’s History SQLite DB (the History file), i.e. an entry in urls and a corresponding visits record.

Why:

  • Gmail route changes are typically same-document (pushState/hash), but the URL is different.
  • The suppression you pasted only disables history when it’s same-document + no user gesture + same URL (classic replaceState() spam case). With Inbox → Drafts, params.url == previous_main_frame_url is false, and the click is a user gesture, so should_update_history_ stays true and the History backend records it.

Caveats where it wouldn’t show up in the SQLite History DB:

  • Incognito (no history persistence).
  • The navigation is in a non-primary page/frame tree (prerender/fenced/etc.).
  • The renderer sets params.should_update_history = false for that commit (less common for a user-click that changes the URL, but possible).
  • Enterprise/policy settings that disable history.

One more nuance: this is separate from session history (Back/Forward). Session history entries live in the session restore files (e.g., Current Session / Current Tabs), not the History SQLite DB — but in the Gmail Inbox→Drafts case, you typically get both (session entry + History DB visit).

Pergunta

yes i know, also i have no policy, no icognito, normal chrome profile

Pensamento

Clarifying Chrome history recording behavior

To confirm if a visit appears in Chrome’s History, keep in mind that this depends on settings like history being enabled and syncing. Chrome’s "Allow Chrome to save browsing history" can be turned off, and same-document navigations may be treated as new visits or update existing visits. To verify, you can query the History SQLite file or check via chrome://history.

Clarifying how Chrome handles fragment URLs in history

Chrome does store full URLs, including fragments (the part after the #). Typically, if you're navigating via a link or auto-bookmark, this will appear in history. For same-document navigation, there’s no new entry unless needed, but a record may still be added if certain conditions apply, like did_replace_entry.

Guidance on verifying Chrome history entries

Yes, you should see visits listed in Chrome’s history. If the same URL already exists, new visits just reference the existing URL, without adding a new row. I can provide a simple SQLite query that you can use to check. Also, it's good to close Chrome before accessing the History file or use the History-journal. I'll include paths for Mac, Windows, and Linux just in case.

Thought for 17s

Resposta

Yep — in a normal (non-incognito) profile with history enabled, Gmail Inbox → Drafts where the URL changes will normally be persisted to Chrome’s History SQLite DB as a visit.

What happens in the DB:

  • If you’ve never visited that exact URL before, Chrome will add it to urls.
  • Each time you navigate there (and Chrome decides to log it), it adds a row to visits referencing that urls.id.
  • Your pasted suppression case only turns history logging off for same-document + no user gesture + same URL. Inbox → Drafts fails the “same URL” check (and typically has a user gesture), so it gets logged.

How to verify quickly (copy the History file first; close Chrome or copy it while Chrome is running so you’re not reading the live-locked DB):

sql
-- Recent visits (with readable timestamps) SELECT u.url, datetime(v.visit_time/1000000 - 11644473600, 'unixepoch') AS ts, v.transition FROM visits v JOIN urls u ON u.id = v.url ORDER BY v.visit_time DESC LIMIT 50;

If Gmail is using hash routes (#inbox#drafts) or pushState, you should see those distinct URLs show up in urls.url and new visits rows when you click around.

If you ever see it not show up, the usual explanations (given your “no policy / no incognito” setup) would be: the navigation was a replaceState to the same URL without a gesture, or history was disabled at the Chrome setting level (rare if you’re seeing other sites logged normally).

Pergunta

and if iw as on inbox and i clicked/swiped to drafts, now the ui has fully updated to drafts view from inbox view, this would change the url? i checked with ane xpert on a forum and he said yes too, but tell me

Pensamento

Confirming Gmail URL changes

Yes, when Gmail switches from one label to another, like from Inbox to Drafts, the URL typically changes. Gmail uses URL fragments like #inbox or #drafts, and clicking or swiping triggers this change. If the URL doesn't update, it might be due to a different UI behavior, like using a split pane.

Clarifying Gmail URL changes

When shifting from Inbox to Drafts in Gmail, the URL updates, either with a fragment (like #drafts) or a new path. Gmail relies on fragments for navigation in its single-page application (SPA) environment, so if the UI switches fully to Drafts, the hash should change. Exceptions could include mobile or simplified views, though URL changes still occur. You can check location.href or use developer tools for confirmation.

Thought for 19s

Resposta

On Gmail in Chrome (web UI), if you truly switch views from Inbox → Drafts, the address bar URL will normally change—most commonly by changing the hash/fragment router from something like:

  • https://mail.google.com/mail/u/0/#inboxhttps://mail.google.com/mail/u/0/#drafts

Gmail uses the #… portion to indicate which label/view to load, and swapping #inbox to #drafts behaves the same as clicking Drafts in the left sidebar (it updates the UI without a full page reload). (How-To Geek)

When it might not change (rare for “Inbox → Drafts”)

If the UI looks different but you didn’t actually switch labels (e.g., you opened a pane/overlay, changed a filter inside Inbox, or some experimental UI element), the URL might not change. But for a real label change to Drafts, Gmail almost always updates the fragment (or another routing part of the URL). (How-To Geek)

Compartilhar esta Q&A