Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
[Backport] Rather than dropping stale navigations, resurrect the navi…
Browse files Browse the repository at this point in the history
…gation entry that they were going to.

This reverts r307614 (which drops navigations) and solves the problem with resurrection.

BUG=501515, 458361, 500576, 86758, 102408, 145969
TEST=covered by tests, as well as it shouldn't regress the original bug 86758

Review URL: https://codereview.chromium.org/1183143006

Cr-Commit-Position: refs/heads/master@{#335212}
(cherry picked from commit 5cad491)

BUG=XWALK-4378
  • Loading branch information
avi authored and Olli Raula committed Aug 24, 2015
1 parent cd15350 commit 12e3aba
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 178 deletions.
71 changes: 18 additions & 53 deletions content/browser/frame_host/navigation_controller_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -994,44 +994,9 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
rfh->GetSiteInstance(),
params.page_id);
if (existing_entry_index == -1) {
// The page was not found. It could have been pruned because of the limit on
// back/forward entries (not likely since we'll usually tell it to navigate
// to such entries). It could also mean that the renderer is smoking crack.
NOTREACHED();

// Because the unknown entry has committed, we risk showing the wrong URL in
// release builds. Instead, we'll kill the renderer process to be safe.
LOG(ERROR) << "terminating renderer for bad navigation: " << params.url;
RecordAction(base::UserMetricsAction("BadMessageTerminate_NC"));

// Temporary code so we can get more information. Format:
// http://url/foo.html#page1#max3#frame1#ids:2_Nx,1_1x,3_2
std::string temp = params.url.spec();
temp.append("#page");
temp.append(base::IntToString(params.page_id));
temp.append("#max");
temp.append(base::IntToString(delegate_->GetMaxPageID()));
temp.append("#frame");
temp.append(base::IntToString(rfh->GetRoutingID()));
temp.append("#ids");
for (int i = 0; i < static_cast<int>(entries_.size()); ++i) {
// Append entry metadata (e.g., 3_7x):
// 3: page_id
// 7: SiteInstance ID, or N for null
// x: appended if not from the current SiteInstance
temp.append(base::IntToString(entries_[i]->GetPageID()));
temp.append("_");
if (entries_[i]->site_instance())
temp.append(base::IntToString(entries_[i]->site_instance()->GetId()));
else
temp.append("N");
if (entries_[i]->site_instance() != rfh->GetSiteInstance())
temp.append("x");
temp.append(",");
}
GURL url(temp);
rfh->render_view_host()->Send(new ViewMsg_TempCrashWithData(url));
return NAVIGATION_TYPE_NAV_IGNORE;
// The renderer has committed a navigation to an entry that no longer
// exists. Because the renderer is showing that page, resurrect that entry.
return NAVIGATION_TYPE_NEW_PAGE;
}
NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get();

Expand Down Expand Up @@ -1181,12 +1146,9 @@ NavigationType NavigationControllerImpl::ClassifyNavigationWithoutPageID(
// Now we know that the notification is for an existing page. Find that entry.
int existing_entry_index = GetEntryIndexWithUniqueID(params.nav_entry_id);
if (existing_entry_index == -1) {
// The page was not found. It could have been pruned because of the limit on
// back/forward entries (not likely since we'll usually tell it to navigate
// to such entries). It could also mean that the renderer is smoking crack.
// TODO(avi): Crash the renderer like we do in the old ClassifyNavigation?
NOTREACHED() << "Could not find nav entry with id " << params.nav_entry_id;
return NAVIGATION_TYPE_NAV_IGNORE;
// The renderer has committed a navigation to an entry that no longer
// exists. Because the renderer is showing that page, resurrect that entry.
return NAVIGATION_TYPE_NEW_PAGE;
}

// Any top-level navigations with the same base (minus the reference fragment)
Expand All @@ -1213,8 +1175,11 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage(
bool update_virtual_url;
// Only make a copy of the pending entry if it is appropriate for the new page
// that was just loaded. We verify this at a coarse grain by checking that
// the SiteInstance hasn't been assigned to something else.
if (pending_entry_ &&
// the SiteInstance hasn't been assigned to something else, and by making sure
// that the pending entry was intended as a new entry (rather than being a
// history navigation that was interrupted by an unrelated, renderer-initiated
// navigation).
if (pending_entry_ && pending_entry_index_ == -1 &&
(!pending_entry_->site_instance() ||
pending_entry_->site_instance() == rfh->GetSiteInstance())) {
new_entry = pending_entry_->Clone();
Expand Down Expand Up @@ -1755,13 +1720,13 @@ void NavigationControllerImpl::InsertOrReplaceEntry(NavigationEntryImpl* entry,
bool replace) {
DCHECK(entry->GetTransitionType() != ui::PAGE_TRANSITION_AUTO_SUBFRAME);

// Copy the pending entry's unique ID to the committed entry.
// I don't know if pending_entry_index_ can be other than -1 here.
const NavigationEntryImpl* const pending_entry =
(pending_entry_index_ == -1) ?
pending_entry_ : entries_[pending_entry_index_].get();
if (pending_entry)
entry->set_unique_id(pending_entry->GetUniqueID());
// If the pending_entry_index_ is -1, the navigation was to a new page, and we
// need to keep continuity with the pending entry, so copy the pending entry's
// unique ID to the committed entry. If the pending_entry_index_ isn't -1,
// then the renderer navigated on its own, independent of the pending entry,
// so don't copy anything.
if (pending_entry_ && pending_entry_index_ == -1)
entry->set_unique_id(pending_entry_->GetUniqueID());

DiscardNonCommittedEntriesInternal();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,31 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadDataWithBaseURL) {
EXPECT_EQ(controller.GetVisibleEntry()->GetOriginalRequestURL(), history_url);
}

// The renderer uses the position in the history list as a clue to whether a
// navigation is stale. In the case where the entry limit is reached and the
// history list is pruned, make sure that there is no mismatch that would cause
// it to start incorrectly rejecting navigations as stale. See
// http://crbug.com/89798.
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, UniqueIDs) {
const NavigationControllerImpl& controller =
static_cast<const NavigationControllerImpl&>(
shell()->web_contents()->GetController());

GURL main_url(embedded_test_server()->GetURL(
"/navigation_controller/page_with_link_to_load_iframe.html"));
NavigateToURL(shell(), main_url);
ASSERT_EQ(1, controller.GetEntryCount());

// Use JavaScript to click the link and load the iframe.
std::string script = "document.getElementById('link').click()";
EXPECT_TRUE(content::ExecuteScript(shell()->web_contents(), script));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
ASSERT_EQ(2, controller.GetEntryCount());

// Unique IDs should... um... be unique.
ASSERT_NE(controller.GetEntryAtIndex(0)->GetUniqueID(),
controller.GetEntryAtIndex(1)->GetUniqueID());
}

// This test used to make sure that a scheme used to prevent spoofs didn't ever
// interfere with navigations. We switched to a different scheme, so now this is
// just a test to make sure we can still navigate once we prune the history
// list.
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
DontIgnoreBackAfterNavEntryLimit) {
NavigationController& controller =
Expand Down
62 changes: 62 additions & 0 deletions content/browser/frame_host/navigation_controller_impl_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4894,4 +4894,66 @@ TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
}
}

// Tests that if a stale navigation comes back from the renderer, it is properly
// resurrected.
TEST_F(NavigationControllerTest, StaleNavigationsResurrected) {
NavigationControllerImpl& controller = controller_impl();
TestNotificationTracker notifications;
RegisterForAllNavNotifications(&notifications, &controller);

// Start on page A.
const GURL url_a("http://foo.com/a");
main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(1, controller.GetEntryCount());
EXPECT_EQ(0, controller.GetCurrentEntryIndex());

// Go to page B.
const GURL url_b("http://foo.com/b");
main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(2, controller.GetEntryCount());
EXPECT_EQ(1, controller.GetCurrentEntryIndex());
int b_entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
int b_page_id = controller.GetLastCommittedEntry()->GetPageID();

// Back to page A.
controller.GoBack();
contents()->CommitPendingNavigation();
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(2, controller.GetEntryCount());
EXPECT_EQ(0, controller.GetCurrentEntryIndex());

// Start going forward to page B.
controller.GoForward();

// But the renderer unilaterally navigates to page C, pruning B.
const GURL url_c("http://foo.com/c");
main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(2, controller.GetEntryCount());
EXPECT_EQ(1, controller.GetCurrentEntryIndex());
int c_entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
EXPECT_NE(c_entry_id, b_entry_id);

// And then the navigation to B gets committed.
main_test_rfh()->SendNavigate(b_page_id, b_entry_id, false, url_b);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;

// Even though we were doing a history navigation, because the entry was
// pruned it will end up as a *new* entry at the end of the entry list. This
// means that occasionally a navigation conflict will end up with one entry
// bubbling to the end of the entry list, but that's the least-bad option.
EXPECT_EQ(3, controller.GetEntryCount());
EXPECT_EQ(2, controller.GetCurrentEntryIndex());
EXPECT_EQ(url_a, controller.GetEntryAtIndex(0)->GetURL());
EXPECT_EQ(url_c, controller.GetEntryAtIndex(1)->GetURL());
EXPECT_EQ(url_b, controller.GetEntryAtIndex(2)->GetURL());
}

} // namespace content
4 changes: 0 additions & 4 deletions content/common/view_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -895,10 +895,6 @@ IPC_MESSAGE_ROUTED2(ViewMsg_SavePageAsMHTML,
int /* job_id */,
IPC::PlatformFileForTransit /* file handle */)

// Temporary message to diagnose an unexpected condition in WebContentsImpl.
IPC_MESSAGE_CONTROL1(ViewMsg_TempCrashWithData,
GURL /* data */)

// An acknowledge to ViewHostMsg_MultipleTargetsTouched to notify the renderer
// process to release the magnified image.
IPC_MESSAGE_ROUTED1(ViewMsg_ReleaseDisambiguationPopupBitmap,
Expand Down
34 changes: 9 additions & 25 deletions content/renderer/render_frame_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4139,12 +4139,8 @@ void RenderFrameImpl::OnFailedNavigation(
bool is_history_navigation = request_params.page_state.IsValid();
WebURLRequest::CachePolicy cache_policy =
WebURLRequest::UseProtocolCachePolicy;
if (!RenderFrameImpl::PrepareRenderViewForNavigation(
common_params.url, is_history_navigation, request_params, &is_reload,
&cache_policy)) {
Send(new FrameHostMsg_DidDropNavigation(routing_id_));
return;
}
RenderFrameImpl::PrepareRenderViewForNavigation(
common_params.url, request_params, &is_reload, &cache_policy);

GetContentClient()->SetActiveURL(common_params.url);

Expand All @@ -4166,6 +4162,8 @@ void RenderFrameImpl::OnFailedNavigation(
SendFailedProvisionalLoad(failed_request, error, frame_);

if (!ShouldDisplayErrorPageForFailedLoad(error_code, common_params.url)) {
// TODO(avi): Remove this; we shouldn't ever be dropping navigations.
// http://crbug.com/501960
Send(new FrameHostMsg_DidDropNavigation(routing_id_));
return;
}
Expand Down Expand Up @@ -4470,12 +4468,8 @@ void RenderFrameImpl::NavigateInternal(
bool is_history_navigation = request_params.page_state.IsValid();
WebURLRequest::CachePolicy cache_policy =
WebURLRequest::UseProtocolCachePolicy;
if (!RenderFrameImpl::PrepareRenderViewForNavigation(
common_params.url, is_history_navigation, request_params, &is_reload,
&cache_policy)) {
Send(new FrameHostMsg_DidDropNavigation(routing_id_));
return;
}
RenderFrameImpl::PrepareRenderViewForNavigation(
common_params.url, request_params, &is_reload, &cache_policy);

GetContentClient()->SetActiveURL(common_params.url);

Expand Down Expand Up @@ -4694,9 +4688,8 @@ RenderFrameImpl::CreateRendererFactory() {
#endif
}

bool RenderFrameImpl::PrepareRenderViewForNavigation(
void RenderFrameImpl::PrepareRenderViewForNavigation(
const GURL& url,
bool is_history_navigation,
const RequestNavigationParams& request_params,
bool* is_reload,
WebURLRequest::CachePolicy* cache_policy) {
Expand All @@ -4707,15 +4700,6 @@ bool RenderFrameImpl::PrepareRenderViewForNavigation(
FOR_EACH_OBSERVER(
RenderViewObserver, render_view_->observers_, Navigate(url));

// If this is a stale back/forward (due to a recent navigation the browser
// didn't know about), ignore it. Only check if swapped in because if the
// frame is swapped out, it won't commit before asking the browser.
if (!render_view_->is_swapped_out() && is_history_navigation &&
render_view_->history_list_offset_ !=
request_params.current_history_list_offset) {
return false;
}

render_view_->history_list_offset_ =
request_params.current_history_list_offset;
render_view_->history_list_length_ =
Expand All @@ -4726,7 +4710,7 @@ bool RenderFrameImpl::PrepareRenderViewForNavigation(
}

if (!is_swapped_out_ || frame_->parent())
return true;
return;

// This is a swapped out main frame, so swap the renderer back in.
// We marked the view as hidden when swapping the view out, so be sure to
Expand All @@ -4748,7 +4732,7 @@ bool RenderFrameImpl::PrepareRenderViewForNavigation(

render_view_->SetSwappedOut(false);
is_swapped_out_ = false;
return true;
return;
}

void RenderFrameImpl::BeginNavigation(blink::WebURLRequest* request) {
Expand Down
6 changes: 2 additions & 4 deletions content/renderer/render_frame_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -770,11 +770,9 @@ class CONTENT_EXPORT RenderFrameImpl
// The method is virtual so that layouttests can override it.
virtual scoped_ptr<MediaStreamRendererFactory> CreateRendererFactory();

// Checks that the RenderView is ready to display the navigation to |url|. If
// the return value is false, the navigation should be abandoned.
bool PrepareRenderViewForNavigation(
// Does preparation for the navigation to |url|.
void PrepareRenderViewForNavigation(
const GURL& url,
bool is_history_navigation,
const RequestNavigationParams& request_params,
bool* is_reload,
blink::WebURLRequest::CachePolicy* cache_policy);
Expand Down
6 changes: 0 additions & 6 deletions content/renderer/render_thread_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1524,7 +1524,6 @@ bool RenderThreadImpl::OnControlMessageReceived(const IPC::Message& msg) {
// is there a new non-windows message I should add here?
IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
IPC_MESSAGE_HANDLER(ViewMsg_NetworkTypeChanged, OnNetworkTypeChanged)
IPC_MESSAGE_HANDLER(ViewMsg_TempCrashWithData, OnTempCrashWithData)
IPC_MESSAGE_HANDLER(WorkerProcessMsg_CreateWorker, OnCreateNewSharedWorker)
IPC_MESSAGE_HANDLER(ViewMsg_TimezoneChange, OnUpdateTimezone)
#if defined(OS_ANDROID)
Expand Down Expand Up @@ -1684,11 +1683,6 @@ void RenderThreadImpl::OnNetworkTypeChanged(
NetConnectionTypeToWebConnectionType(type));
}

void RenderThreadImpl::OnTempCrashWithData(const GURL& data) {
GetContentClient()->SetActiveURL(data);
CHECK(false);
}

void RenderThreadImpl::OnUpdateTimezone(const std::string& zone_id) {
if (!blink_platform_impl_)
return;
Expand Down
1 change: 0 additions & 1 deletion content/renderer/render_thread_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,6 @@ class CONTENT_EXPORT RenderThreadImpl
#endif
void OnNetworkTypeChanged(net::NetworkChangeNotifier::ConnectionType type);
void OnGetAccessibilityTree();
void OnTempCrashWithData(const GURL& data);
void OnUpdateTimezone(const std::string& zoneId);
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
Expand Down
Loading

0 comments on commit 12e3aba

Please sign in to comment.