diff options
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(); |