diff options
author | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-02 19:23:45 +0000 |
---|---|---|
committer | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-02 19:23:45 +0000 |
commit | 15d79e12a0831ac527dc938297c14a386dcf5f0a (patch) | |
tree | 22c72b70cd8d4534433a1005b5e680c66eeb9463 /chrome/renderer/render_view.cc | |
parent | 30de967447ec9f048578cda8b22aba5fad2c68d8 (diff) | |
download | chromium_src-15d79e12a0831ac527dc938297c14a386dcf5f0a.zip chromium_src-15d79e12a0831ac527dc938297c14a386dcf5f0a.tar.gz chromium_src-15d79e12a0831ac527dc938297c14a386dcf5f0a.tar.bz2 |
Move alternate error page loading out of WebFrame.
Make the RenderView be in charge of loading alternate error pages.
While working on this change, I noticed several related bugs:
1- Loading an URL with an invalid host name from the new tab page results in
an error page. If you hit back and then forward, you will be left with an
empty location bar. In a debug build this trips an assertion in
ClassifyNavigation because the given page_id is -1. This problem is caused by
not duplicating the NavigationState of the failed load when creating a load for
the error page. Hence, the pending_page_id of the forward navigation is lost.
2- Loading an URL with an invalid host name as a subframe results in an extra
navigation in session history. One navigation for the main frame and one
navigation for the error page load. This is another symptom of the problem
described in #1. However, the solution is different. Here, we need to know
that the subframe load is an AUTO_SUBFRAME load so that we load the error page
using 'replace' semantics (so that WebCore does not generate a new session
history entry).
Finally, I decided to restrict alternative DNS error pages to only work for the
main frame to match what we do for alternative 404 error pages. It doesn't seem
worth it to show link doctor results for subframes since their primary purpose
is to assist people who mis-type an URL.
R=tony,brettw
BUG=15648
TEST=covered by errorpage_uitest.cc
Review URL: http://codereview.chromium.org/159575
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22253 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/render_view.cc')
-rw-r--r-- | chrome/renderer/render_view.cc | 117 |
1 files changed, 88 insertions, 29 deletions
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 2c805ac..91126bd 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -98,6 +98,7 @@ using base::Time; using base::TimeDelta; +using webkit_glue::AltErrorPageResourceFetcher; using webkit_glue::AutofillForm; using webkit_glue::PasswordForm; using webkit_glue::PasswordFormDomManager; @@ -1140,6 +1141,10 @@ void RenderView::DidStartProvisionalLoadForFrame( // Make sure redirect tracking state is clear for the new load. completed_client_redirect_src_ = GURL(); + } else if (frame->GetParent()->IsLoading()) { + // Take note of AUTO_SUBFRAME loads here, so that we can know how to + // load an error page. See DidFailProvisionalLoadWithError. + navigation_state->set_transition_type(PageTransition::AUTO_SUBFRAME); } Send(new ViewHostMsg_DidStartProvisionalLoadForFrame( @@ -1211,33 +1216,33 @@ void RenderView::DidFailProvisionalLoadWithError(WebView* webview, // Make sure we never show errors in view source mode. frame->SetInViewSourceMode(false); + NavigationState* navigation_state = NavigationState::FromDataSource(ds); + // If this is a failed back/forward/reload navigation, then we need to do a // 'replace' load. This is necessary to avoid messing up session history. // Otherwise, we do a normal load, which simulates a 'go' navigation as far // as session history is concerned. - bool replace = !NavigationState::FromDataSource(ds)->is_new_navigation(); + // + // AUTO_SUBFRAME loads should always be treated as loads that do not advance + // the page id. + // + bool replace = + navigation_state->pending_page_id() != -1 || + navigation_state->transition_type() == PageTransition::AUTO_SUBFRAME; - // Use the alternate error page service if this is a DNS failure or - // connection failure. ERR_CONNECTION_FAILED can be dropped once we no longer - // use winhttp. - int ec = error.reason; - if (ec == net::ERR_NAME_NOT_RESOLVED || - ec == net::ERR_CONNECTION_FAILED || - ec == net::ERR_CONNECTION_REFUSED || - ec == net::ERR_ADDRESS_UNREACHABLE || - ec == net::ERR_TIMED_OUT) { - const GURL& failed_url = error.unreachableURL; - const GURL& error_page_url = GetAlternateErrorPageURL(failed_url, - ec == net::ERR_NAME_NOT_RESOLVED ? WebViewDelegate::DNS_ERROR - : WebViewDelegate::CONNECTION_ERROR); - if (error_page_url.is_valid()) { - // Ask the WebFrame to fetch the alternate error page for us. - frame->LoadAlternateHTMLErrorPage(failed_request, error, error_page_url, - replace, GURL(kUnreachableWebDataURL)); - return; - } + // If we failed on a browser initiated request, then make sure that our error + // page load is regarded as the same browser initiated request. + if (!navigation_state->is_content_initiated()) { + pending_navigation_state_.reset(NavigationState::CreateBrowserInitiated( + navigation_state->pending_page_id(), + navigation_state->transition_type(), + navigation_state->request_time())); } + // Provide the user with a more helpful error page? + if (MaybeLoadAlternateErrorPage(frame, error, replace)) + return; + // Fallback to a local error page. LoadNavigationErrorPage(frame, failed_request, error, std::string(), replace); @@ -1297,19 +1302,19 @@ void RenderView::DidCommitLoadForFrame(WebView *webview, WebFrame* frame, page_id_, true), kDelayForForcedCaptureMs); } else { - // Inspect the navigation_state on the main frame (set in our Navigate - // method) to see if the navigation corresponds to a session history - // navigation... Note: |frame| may or may not be the toplevel frame, but - // for the case of capturing session history, the first committed frame - // suffices. We keep track of whether we've seen this commit before so - // that only capture session history once per navigation. + // Inspect the navigation_state on this frame to see if the navigation + // corresponds to a session history navigation... Note: |frame| may or + // may not be the toplevel frame, but for the case of capturing session + // history, the first committed frame suffices. We keep track of whether + // we've seen this commit before so that only capture session history once + // per navigation. // // Note that we need to check if the page ID changed. In the case of a // reload, the page ID doesn't change, and UpdateSessionHistory gets the // previous URL and the current page ID, which would be wrong. - if (!navigation_state->is_new_navigation() && - !navigation_state->request_committed() && - page_id_ != navigation_state->pending_page_id()) { + if (navigation_state->pending_page_id() != -1 && + navigation_state->pending_page_id() != page_id_ && + !navigation_state->request_committed()) { // This is a successful session history navigation! UpdateSessionHistory(frame); page_id_ = navigation_state->pending_page_id(); @@ -2827,6 +2832,53 @@ void RenderView::OnDisassociateFromPopupCount() { decrement_shared_popup_at_destruction_ = false; } +bool RenderView::MaybeLoadAlternateErrorPage(WebFrame* frame, + const WebURLError& error, + bool replace) { + // We only show alternate error pages in the main frame. They are + // intended to assist the user when navigating, so there is not much + // value in showing them for failed subframes. Ideally, we would be + // able to use the TYPED transition type for this, but that flag is + // not preserved across page reloads. + if (frame->GetParent()) + return false; + + // Use the alternate error page service if this is a DNS failure or + // connection failure. ERR_CONNECTION_FAILED can be dropped once we no + // longer use winhttp. + int ec = error.reason; + if (ec != net::ERR_NAME_NOT_RESOLVED && + ec != net::ERR_CONNECTION_FAILED && + ec != net::ERR_CONNECTION_REFUSED && + ec != net::ERR_ADDRESS_UNREACHABLE && + ec != net::ERR_TIMED_OUT) + return false; + + const GURL& error_page_url = GetAlternateErrorPageURL(error.unreachableURL, + ec == net::ERR_NAME_NOT_RESOLVED ? WebViewDelegate::DNS_ERROR + : WebViewDelegate::CONNECTION_ERROR); + if (!error_page_url.is_valid()) + return false; + + // Load an empty page first so there is an immediate response to the error, + // and then kick off a request for the alternate error page. + frame->LoadHTMLString(std::string(), + GURL(kUnreachableWebDataURL), + error.unreachableURL, + replace); + + // Now, create a fetcher for the error page and associate it with the data + // source we just created via the LoadHTMLString call. That way if another + // navigation occurs, the fetcher will get destroyed. + NavigationState* navigation_state = + NavigationState::FromDataSource(frame->GetProvisionalDataSource()); + navigation_state->set_alt_error_page_fetcher( + new AltErrorPageResourceFetcher( + error_page_url, frame, error, + NewCallback(this, &RenderView::AltErrorPageFinished))); + return true; +} + std::string RenderView::GetAltHTMLForTemplate( const DictionaryValue& error_strings, int template_resource_id) const { const StringPiece template_html( @@ -2843,6 +2895,13 @@ std::string RenderView::GetAltHTMLForTemplate( template_html, &error_strings, "t"); } +void RenderView::AltErrorPageFinished(WebFrame* frame, + const WebURLError& original_error, + const std::string& html) { + // Here, we replace the blank page we loaded previously. + LoadNavigationErrorPage(frame, WebURLRequest(), original_error, html, true); +} + void RenderView::OnMoveOrResizeStarted() { if (webview()) webview()->HideAutofillPopup(); |