1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/net/net_error_helper_core.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/i18n/rtl.h"
14 #include "base/json/json_reader.h"
15 #include "base/json/json_value_converter.h"
16 #include "base/json/json_writer.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_vector.h"
20 #include "base/metrics/histogram.h"
21 #include "base/strings/string16.h"
22 #include "base/strings/string_util.h"
23 #include "base/values.h"
24 #include "chrome/common/localized_error.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "content/public/common/url_constants.h"
27 #include "net/base/escape.h"
28 #include "net/base/net_errors.h"
29 #include "net/base/net_util.h"
30 #include "third_party/WebKit/public/platform/WebString.h"
31 #include "third_party/WebKit/public/platform/WebURLError.h"
32 #include "ui/base/l10n/l10n_util.h"
37 struct CorrectionTypeToResourceTable
{
39 const char* correction_type
;
42 const CorrectionTypeToResourceTable kCorrectionResourceTable
[] = {
43 {IDS_ERRORPAGES_SUGGESTION_VISIT_GOOGLE_CACHE
, "cachedPage"},
44 // "reloadPage" is has special handling.
45 {IDS_ERRORPAGES_SUGGESTION_CORRECTED_URL
, "urlCorrection"},
46 {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL
, "siteDomain"},
47 {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL
, "host"},
48 {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL
, "sitemap"},
49 {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL
, "pathParentFolder"},
50 // "siteSearchQuery" is not yet supported.
51 // TODO(mmenke): Figure out what format "siteSearchQuery" uses for its
53 // "webSearchQuery" has special handling.
54 {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL
, "contentOverlap"},
55 {IDS_ERRORPAGES_SUGGESTION_CORRECTED_URL
, "emphasizedUrlCorrection"},
58 struct NavigationCorrection
{
59 NavigationCorrection() : is_porn(false), is_soft_porn(false) {
62 static void RegisterJSONConverter(
63 base::JSONValueConverter
<NavigationCorrection
>* converter
) {
64 converter
->RegisterStringField("correctionType",
65 &NavigationCorrection::correction_type
);
66 converter
->RegisterStringField("urlCorrection",
67 &NavigationCorrection::url_correction
);
68 converter
->RegisterStringField("clickType",
69 &NavigationCorrection::click_type
);
70 converter
->RegisterStringField("clickData",
71 &NavigationCorrection::click_data
);
72 converter
->RegisterBoolField("isPorn", &NavigationCorrection::is_porn
);
73 converter
->RegisterBoolField("isSoftPorn",
74 &NavigationCorrection::is_soft_porn
);
77 std::string correction_type
;
78 std::string url_correction
;
79 std::string click_type
;
80 std::string click_data
;
85 struct NavigationCorrectionResponse
{
87 std::string fingerprint
;
88 ScopedVector
<NavigationCorrection
> corrections
;
90 static void RegisterJSONConverter(
91 base::JSONValueConverter
<NavigationCorrectionResponse
>* converter
) {
92 converter
->RegisterStringField("result.eventId",
93 &NavigationCorrectionResponse::event_id
);
94 converter
->RegisterStringField("result.fingerprint",
95 &NavigationCorrectionResponse::fingerprint
);
96 converter
->RegisterRepeatedMessage(
97 "result.UrlCorrections",
98 &NavigationCorrectionResponse::corrections
);
102 base::TimeDelta
GetAutoReloadTime(size_t reload_count
) {
103 static const int kDelaysMs
[] = {
104 0, 5000, 30000, 60000, 300000, 600000, 1800000
106 if (reload_count
>= arraysize(kDelaysMs
))
107 reload_count
= arraysize(kDelaysMs
) - 1;
108 return base::TimeDelta::FromMilliseconds(kDelaysMs
[reload_count
]);
111 // Returns whether |net_error| is a DNS-related error (and therefore whether
112 // the tab helper should start a DNS probe after receiving it.)
113 bool IsDnsError(const blink::WebURLError
& error
) {
114 return error
.domain
.utf8() == net::kErrorDomain
&&
115 (error
.reason
== net::ERR_NAME_NOT_RESOLVED
||
116 error
.reason
== net::ERR_NAME_RESOLUTION_FAILED
);
119 GURL
SanitizeURL(const GURL
& url
) {
120 GURL::Replacements remove_params
;
121 remove_params
.ClearUsername();
122 remove_params
.ClearPassword();
123 remove_params
.ClearQuery();
124 remove_params
.ClearRef();
125 return url
.ReplaceComponents(remove_params
);
128 // Sanitizes and formats a URL for upload to the error correction service.
129 std::string
PrepareUrlForUpload(const GURL
& url
) {
130 // TODO(yuusuke): Change to net::FormatUrl when Link Doctor becomes
132 std::string spec_to_send
= SanitizeURL(url
).spec();
134 // Notify navigation correction service of the url truncation by sending of
137 spec_to_send
.append("?");
141 // Given a WebURLError, returns true if the FixURL service should be used
142 // for that error. Also sets |error_param| to the string that should be sent to
143 // the FixURL service to identify the error type.
144 bool ShouldUseFixUrlServiceForError(const blink::WebURLError
& error
,
145 std::string
* error_param
) {
146 error_param
->clear();
148 // Don't use the correction service for HTTPS (for privacy reasons).
149 GURL
unreachable_url(error
.unreachableURL
);
150 if (GURL(unreachable_url
).SchemeIsSecure())
153 std::string domain
= error
.domain
.utf8();
154 if (domain
== "http" && error
.reason
== 404) {
155 *error_param
= "http404";
158 if (IsDnsError(error
)) {
159 *error_param
= "dnserror";
162 if (domain
== net::kErrorDomain
&&
163 (error
.reason
== net::ERR_CONNECTION_FAILED
||
164 error
.reason
== net::ERR_CONNECTION_REFUSED
||
165 error
.reason
== net::ERR_ADDRESS_UNREACHABLE
||
166 error
.reason
== net::ERR_CONNECTION_TIMED_OUT
)) {
167 *error_param
= "connectionFailure";
173 // Creates a request body for use with the fixurl service. Sets parameters
174 // shared by all types of requests to the service. |correction_params| must
175 // contain the parameters specific to the actual request type.
176 std::string
CreateRequestBody(
177 const std::string
& method
,
178 const std::string
& error_param
,
179 const NetErrorHelperCore::NavigationCorrectionParams
& correction_params
,
180 scoped_ptr
<base::DictionaryValue
> params_dict
) {
181 // Set params common to all request types.
182 params_dict
->SetString("key", correction_params
.api_key
);
183 params_dict
->SetString("clientName", "chrome");
184 params_dict
->SetString("error", error_param
);
186 if (!correction_params
.language
.empty())
187 params_dict
->SetString("language", correction_params
.language
);
189 if (!correction_params
.country_code
.empty())
190 params_dict
->SetString("originCountry", correction_params
.country_code
);
192 base::DictionaryValue request_dict
;
193 request_dict
.SetString("method", method
);
194 request_dict
.SetString("apiVersion", "v1");
195 request_dict
.Set("params", params_dict
.release());
197 std::string request_body
;
198 bool success
= base::JSONWriter::Write(&request_dict
, &request_body
);
203 // If URL correction information should be retrieved remotely for a main frame
204 // load that failed with |error|, returns true and sets
205 // |correction_request_body| to be the body for the correction request.
206 std::string
CreateFixUrlRequestBody(
207 const blink::WebURLError
& error
,
208 const NetErrorHelperCore::NavigationCorrectionParams
& correction_params
) {
209 std::string error_param
;
210 bool result
= ShouldUseFixUrlServiceForError(error
, &error_param
);
213 // TODO(mmenke): Investigate open sourcing the relevant protocol buffers and
214 // using those directly instead.
215 scoped_ptr
<base::DictionaryValue
> params(new base::DictionaryValue());
216 params
->SetString("urlQuery", PrepareUrlForUpload(error
.unreachableURL
));
217 return CreateRequestBody("linkdoctor.fixurl.fixurl", error_param
,
218 correction_params
, params
.Pass());
221 std::string
CreateClickTrackingUrlRequestBody(
222 const blink::WebURLError
& error
,
223 const NetErrorHelperCore::NavigationCorrectionParams
& correction_params
,
224 const NavigationCorrectionResponse
& response
,
225 const NavigationCorrection
& correction
) {
226 std::string error_param
;
227 bool result
= ShouldUseFixUrlServiceForError(error
, &error_param
);
230 scoped_ptr
<base::DictionaryValue
> params(new base::DictionaryValue());
232 params
->SetString("originalUrlQuery",
233 PrepareUrlForUpload(error
.unreachableURL
));
235 params
->SetString("clickedUrlCorrection", correction
.url_correction
);
236 params
->SetString("clickType", correction
.click_type
);
237 params
->SetString("clickData", correction
.click_data
);
239 params
->SetString("eventId", response
.event_id
);
240 params
->SetString("fingerprint", response
.fingerprint
);
242 return CreateRequestBody("linkdoctor.fixurl.clicktracking", error_param
,
243 correction_params
, params
.Pass());
246 base::string16
FormatURLForDisplay(const GURL
& url
, bool is_rtl
,
247 const std::string accept_languages
) {
248 // Translate punycode into UTF8, unescape UTF8 URLs.
249 base::string16
url_for_display(net::FormatUrl(
250 url
, accept_languages
, net::kFormatUrlOmitNothing
,
251 net::UnescapeRule::NORMAL
, NULL
, NULL
, NULL
));
252 // URLs are always LTR.
254 base::i18n::WrapStringWithLTRFormatting(&url_for_display
);
255 return url_for_display
;
258 scoped_ptr
<NavigationCorrectionResponse
> ParseNavigationCorrectionResponse(
259 const std::string raw_response
) {
260 // TODO(mmenke): Open source related protocol buffers and use them directly.
261 scoped_ptr
<base::Value
> parsed(base::JSONReader::Read(raw_response
));
262 scoped_ptr
<NavigationCorrectionResponse
> response(
263 new NavigationCorrectionResponse());
264 base::JSONValueConverter
<NavigationCorrectionResponse
> converter
;
265 if (!parsed
|| !converter
.Convert(*parsed
, response
.get()))
267 return response
.Pass();
270 scoped_ptr
<LocalizedError::ErrorPageParams
> CreateErrorPageParams(
271 const NavigationCorrectionResponse
& response
,
272 const blink::WebURLError
& error
,
273 const NetErrorHelperCore::NavigationCorrectionParams
& correction_params
,
274 const std::string
& accept_languages
,
276 // Version of URL for display in suggestions. It has to be sanitized first
277 // because any received suggestions will be relative to the sanitized URL.
278 base::string16 original_url_for_display
=
279 FormatURLForDisplay(SanitizeURL(GURL(error
.unreachableURL
)), is_rtl
,
282 scoped_ptr
<LocalizedError::ErrorPageParams
> params(
283 new LocalizedError::ErrorPageParams());
284 params
->override_suggestions
.reset(new base::ListValue());
285 scoped_ptr
<base::ListValue
> parsed_corrections(new base::ListValue());
286 for (ScopedVector
<NavigationCorrection
>::const_iterator it
=
287 response
.corrections
.begin();
288 it
!= response
.corrections
.end(); ++it
) {
289 // Doesn't seem like a good idea to show these.
290 if ((*it
)->is_porn
|| (*it
)->is_soft_porn
)
293 int tracking_id
= it
- response
.corrections
.begin();
295 if ((*it
)->correction_type
== "reloadPage") {
296 params
->suggest_reload
= true;
297 params
->reload_tracking_id
= tracking_id
;
301 if ((*it
)->correction_type
== "webSearchQuery") {
302 // If there are mutliple searches suggested, use the first suggestion.
303 if (params
->search_terms
.empty()) {
304 params
->search_url
= correction_params
.search_url
;
305 params
->search_terms
= (*it
)->url_correction
;
306 params
->search_tracking_id
= tracking_id
;
311 // Allow reload page and web search query to be empty strings, but not
313 if ((*it
)->url_correction
.empty())
315 size_t correction_index
;
316 for (correction_index
= 0;
317 correction_index
< arraysize(kCorrectionResourceTable
);
318 ++correction_index
) {
319 if ((*it
)->correction_type
!=
320 kCorrectionResourceTable
[correction_index
].correction_type
) {
323 base::DictionaryValue
* suggest
= new base::DictionaryValue();
324 suggest
->SetString("header",
325 l10n_util::GetStringUTF16(
326 kCorrectionResourceTable
[correction_index
].resource_id
));
327 suggest
->SetString("urlCorrection", (*it
)->url_correction
);
329 "urlCorrectionForDisplay",
330 FormatURLForDisplay(GURL((*it
)->url_correction
), is_rtl
,
332 suggest
->SetString("originalUrlForDisplay", original_url_for_display
);
333 suggest
->SetInteger("trackingId", tracking_id
);
334 params
->override_suggestions
->Append(suggest
);
339 if (params
->override_suggestions
->empty() && !params
->search_url
.is_valid())
341 return params
.Pass();
344 void ReportAutoReloadSuccess(const blink::WebURLError
& error
, size_t count
) {
345 if (error
.domain
.utf8() != net::kErrorDomain
)
347 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess",
349 net::GetAllErrorCodesForUma());
350 UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess", count
);
352 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess",
354 net::GetAllErrorCodesForUma());
358 void ReportAutoReloadFailure(const blink::WebURLError
& error
, size_t count
) {
359 if (error
.domain
.utf8() != net::kErrorDomain
)
361 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop",
363 net::GetAllErrorCodesForUma());
364 UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop", count
);
369 struct NetErrorHelperCore::ErrorPageInfo
{
370 ErrorPageInfo(blink::WebURLError error
, bool was_failed_post
)
372 was_failed_post(was_failed_post
),
373 needs_dns_updates(false),
374 needs_load_navigation_corrections(false),
375 reload_button_in_page(false),
376 load_stale_button_in_page(false),
377 is_finished_loading(false),
378 auto_reload_triggered(false) {
381 // Information about the failed page load.
382 blink::WebURLError error
;
383 bool was_failed_post
;
385 // Information about the status of the error page.
387 // True if a page is a DNS error page and has not yet received a final DNS
389 bool needs_dns_updates
;
391 // True if a blank page was loaded, and navigation corrections need to be
392 // loaded to generate the real error page.
393 bool needs_load_navigation_corrections
;
395 // Navigation correction service paramers, which will be used in response to
396 // certain types of network errors. They are all stored here in case they
397 // change over the course of displaying the error page.
398 scoped_ptr
<NetErrorHelperCore::NavigationCorrectionParams
>
399 navigation_correction_params
;
401 scoped_ptr
<NavigationCorrectionResponse
> navigation_correction_response
;
403 // All the navigation corrections that have been clicked, for tracking
405 std::set
<int> clicked_corrections
;
407 // Track if specific buttons are included in an error page, for statistics.
408 bool reload_button_in_page
;
409 bool load_stale_button_in_page
;
411 // True if a page has completed loading, at which point it can receive
413 bool is_finished_loading
;
415 // True if the auto-reload timer has fired and a reload is or has been in
417 bool auto_reload_triggered
;
420 NetErrorHelperCore::NavigationCorrectionParams::NavigationCorrectionParams() {
423 NetErrorHelperCore::NavigationCorrectionParams::~NavigationCorrectionParams() {
426 bool NetErrorHelperCore::IsReloadableError(
427 const NetErrorHelperCore::ErrorPageInfo
& info
) {
428 return info
.error
.domain
.utf8() == net::kErrorDomain
&&
429 info
.error
.reason
!= net::ERR_ABORTED
&&
430 !info
.was_failed_post
;
433 NetErrorHelperCore::NetErrorHelperCore(Delegate
* delegate
,
434 bool auto_reload_enabled
,
435 bool auto_reload_visible_only
,
437 : delegate_(delegate
),
438 last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE
),
439 auto_reload_enabled_(auto_reload_enabled
),
440 auto_reload_visible_only_(auto_reload_visible_only
),
441 auto_reload_timer_(new base::Timer(false, false)),
442 auto_reload_paused_(false),
443 uncommitted_load_started_(false),
444 // TODO(ellyjones): Make online_ accurate at object creation.
446 visible_(is_visible
),
447 auto_reload_count_(0),
448 navigation_from_button_(NO_BUTTON
) {
451 NetErrorHelperCore::~NetErrorHelperCore() {
452 if (committed_error_page_info_
&&
453 committed_error_page_info_
->auto_reload_triggered
) {
454 ReportAutoReloadFailure(committed_error_page_info_
->error
,
459 void NetErrorHelperCore::CancelPendingFetches() {
460 // Cancel loading the alternate error page, and prevent any pending error page
461 // load from starting a new error page load. Swapping in the error page when
462 // it's finished loading could abort the navigation, otherwise.
463 if (committed_error_page_info_
)
464 committed_error_page_info_
->needs_load_navigation_corrections
= false;
465 if (pending_error_page_info_
)
466 pending_error_page_info_
->needs_load_navigation_corrections
= false;
467 delegate_
->CancelFetchNavigationCorrections();
468 auto_reload_timer_
->Stop();
469 auto_reload_paused_
= false;
472 void NetErrorHelperCore::OnStop() {
473 if (committed_error_page_info_
&&
474 committed_error_page_info_
->auto_reload_triggered
) {
475 ReportAutoReloadFailure(committed_error_page_info_
->error
,
478 CancelPendingFetches();
479 uncommitted_load_started_
= false;
480 auto_reload_count_
= 0;
483 void NetErrorHelperCore::OnWasShown() {
485 if (!auto_reload_visible_only_
)
487 if (auto_reload_paused_
)
488 MaybeStartAutoReloadTimer();
491 void NetErrorHelperCore::OnWasHidden() {
493 if (!auto_reload_visible_only_
)
495 PauseAutoReloadTimer();
498 void NetErrorHelperCore::OnStartLoad(FrameType frame_type
, PageType page_type
) {
499 if (frame_type
!= MAIN_FRAME
)
502 uncommitted_load_started_
= true;
504 // If there's no pending error page information associated with the page load,
505 // or the new page is not an error page, then reset pending error page state.
506 if (!pending_error_page_info_
|| page_type
!= ERROR_PAGE
)
507 CancelPendingFetches();
510 void NetErrorHelperCore::OnCommitLoad(FrameType frame_type
, const GURL
& url
) {
511 if (frame_type
!= MAIN_FRAME
)
514 // uncommitted_load_started_ could already be false, since RenderFrameImpl
515 // calls OnCommitLoad once for each in-page navigation (like a fragment
516 // change) with no corresponding OnStartLoad.
517 uncommitted_load_started_
= false;
519 // Track if an error occurred due to a page button press.
520 // This isn't perfect; if (for instance), the server is slow responding
521 // to a request generated from the page reload button, and the user hits
522 // the browser reload button, this code will still believe the
523 // result is from the page reload button.
524 if (committed_error_page_info_
&& pending_error_page_info_
&&
525 navigation_from_button_
!= NO_BUTTON
&&
526 committed_error_page_info_
->error
.unreachableURL
==
527 pending_error_page_info_
->error
.unreachableURL
) {
528 DCHECK(navigation_from_button_
== RELOAD_BUTTON
||
529 navigation_from_button_
== LOAD_STALE_BUTTON
);
530 chrome_common_net::RecordEvent(
531 navigation_from_button_
== RELOAD_BUTTON
?
532 chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_ERROR
:
533 chrome_common_net::NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_ERROR
);
535 navigation_from_button_
= NO_BUTTON
;
537 if (committed_error_page_info_
&& !pending_error_page_info_
&&
538 committed_error_page_info_
->auto_reload_triggered
) {
539 const blink::WebURLError
& error
= committed_error_page_info_
->error
;
540 const GURL
& error_url
= error
.unreachableURL
;
541 if (url
== error_url
)
542 ReportAutoReloadSuccess(error
, auto_reload_count_
);
543 else if (url
!= GURL(content::kUnreachableWebDataURL
))
544 ReportAutoReloadFailure(error
, auto_reload_count_
);
547 committed_error_page_info_
.reset(pending_error_page_info_
.release());
550 void NetErrorHelperCore::OnFinishLoad(FrameType frame_type
) {
551 if (frame_type
!= MAIN_FRAME
)
554 if (!committed_error_page_info_
) {
555 auto_reload_count_
= 0;
559 committed_error_page_info_
->is_finished_loading
= true;
561 chrome_common_net::RecordEvent(chrome_common_net::NETWORK_ERROR_PAGE_SHOWN
);
562 if (committed_error_page_info_
->reload_button_in_page
) {
563 chrome_common_net::RecordEvent(
564 chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_SHOWN
);
566 if (committed_error_page_info_
->load_stale_button_in_page
) {
567 chrome_common_net::RecordEvent(
568 chrome_common_net::NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_SHOWN
);
571 delegate_
->EnablePageHelperFunctions();
573 if (committed_error_page_info_
->needs_load_navigation_corrections
) {
574 // If there is another pending error page load, |fix_url| should have been
576 DCHECK(!pending_error_page_info_
);
577 DCHECK(!committed_error_page_info_
->needs_dns_updates
);
578 delegate_
->FetchNavigationCorrections(
579 committed_error_page_info_
->navigation_correction_params
->url
,
580 CreateFixUrlRequestBody(
581 committed_error_page_info_
->error
,
582 *committed_error_page_info_
->navigation_correction_params
));
583 } else if (auto_reload_enabled_
&&
584 IsReloadableError(*committed_error_page_info_
)) {
585 MaybeStartAutoReloadTimer();
588 if (!committed_error_page_info_
->needs_dns_updates
||
589 last_probe_status_
== chrome_common_net::DNS_PROBE_POSSIBLE
) {
592 DVLOG(1) << "Error page finished loading; sending saved status.";
596 void NetErrorHelperCore::GetErrorHTML(
597 FrameType frame_type
,
598 const blink::WebURLError
& error
,
600 std::string
* error_html
) {
601 if (frame_type
== MAIN_FRAME
) {
602 // If navigation corrections were needed before, that should have been
603 // cancelled earlier by starting a new page load (Which has now failed).
604 DCHECK(!committed_error_page_info_
||
605 !committed_error_page_info_
->needs_load_navigation_corrections
);
607 pending_error_page_info_
.reset(new ErrorPageInfo(error
, is_failed_post
));
608 pending_error_page_info_
->navigation_correction_params
.reset(
609 new NavigationCorrectionParams(navigation_correction_params_
));
610 GetErrorHtmlForMainFrame(pending_error_page_info_
.get(), error_html
);
612 // These values do not matter, as error pages in iframes hide the buttons.
613 bool reload_button_in_page
;
614 bool load_stale_button_in_page
;
616 delegate_
->GenerateLocalizedErrorPage(
617 error
, is_failed_post
, scoped_ptr
<LocalizedError::ErrorPageParams
>(),
618 &reload_button_in_page
, &load_stale_button_in_page
,
623 void NetErrorHelperCore::OnNetErrorInfo(
624 chrome_common_net::DnsProbeStatus status
) {
625 DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE
, status
);
627 last_probe_status_
= status
;
629 if (!committed_error_page_info_
||
630 !committed_error_page_info_
->needs_dns_updates
||
631 !committed_error_page_info_
->is_finished_loading
) {
638 void NetErrorHelperCore::OnSetNavigationCorrectionInfo(
639 const GURL
& navigation_correction_url
,
640 const std::string
& language
,
641 const std::string
& country_code
,
642 const std::string
& api_key
,
643 const GURL
& search_url
) {
644 navigation_correction_params_
.url
= navigation_correction_url
;
645 navigation_correction_params_
.language
= language
;
646 navigation_correction_params_
.country_code
= country_code
;
647 navigation_correction_params_
.api_key
= api_key
;
648 navigation_correction_params_
.search_url
= search_url
;
651 void NetErrorHelperCore::GetErrorHtmlForMainFrame(
652 ErrorPageInfo
* pending_error_page_info
,
653 std::string
* error_html
) {
654 std::string error_param
;
655 blink::WebURLError error
= pending_error_page_info
->error
;
657 if (pending_error_page_info
->navigation_correction_params
&&
658 pending_error_page_info
->navigation_correction_params
->url
.is_valid() &&
659 ShouldUseFixUrlServiceForError(error
, &error_param
)) {
660 pending_error_page_info
->needs_load_navigation_corrections
= true;
664 if (IsDnsError(pending_error_page_info
->error
)) {
665 // The last probe status needs to be reset if this is a DNS error. This
666 // means that if a DNS error page is committed but has not yet finished
667 // loading, a DNS probe status scheduled to be sent to it may be thrown
668 // out, but since the new error page should trigger a new DNS probe, it
669 // will just get the results for the next page load.
670 last_probe_status_
= chrome_common_net::DNS_PROBE_POSSIBLE
;
671 pending_error_page_info
->needs_dns_updates
= true;
672 error
= GetUpdatedError(error
);
675 delegate_
->GenerateLocalizedErrorPage(
676 error
, pending_error_page_info
->was_failed_post
,
677 scoped_ptr
<LocalizedError::ErrorPageParams
>(),
678 &pending_error_page_info
->reload_button_in_page
,
679 &pending_error_page_info
->load_stale_button_in_page
,
683 void NetErrorHelperCore::UpdateErrorPage() {
684 DCHECK(committed_error_page_info_
->needs_dns_updates
);
685 DCHECK(committed_error_page_info_
->is_finished_loading
);
686 DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE
, last_probe_status_
);
688 UMA_HISTOGRAM_ENUMERATION("DnsProbe.ErrorPageUpdateStatus",
690 chrome_common_net::DNS_PROBE_MAX
);
691 // Every status other than DNS_PROBE_POSSIBLE and DNS_PROBE_STARTED is a
692 // final status code. Once one is reached, the page does not need further
694 if (last_probe_status_
!= chrome_common_net::DNS_PROBE_STARTED
)
695 committed_error_page_info_
->needs_dns_updates
= false;
697 // There is no need to worry about the button display statistics here because
698 // the presentation of the reload and load stale buttons can't be changed
699 // by a DNS error update.
700 delegate_
->UpdateErrorPage(
701 GetUpdatedError(committed_error_page_info_
->error
),
702 committed_error_page_info_
->was_failed_post
);
705 void NetErrorHelperCore::OnNavigationCorrectionsFetched(
706 const std::string
& corrections
,
707 const std::string
& accept_languages
,
709 // Loading suggestions only starts when a blank error page finishes loading,
710 // and is cancelled with a new load.
711 DCHECK(!pending_error_page_info_
);
712 DCHECK(committed_error_page_info_
->is_finished_loading
);
713 DCHECK(committed_error_page_info_
->needs_load_navigation_corrections
);
714 DCHECK(committed_error_page_info_
->navigation_correction_params
);
716 pending_error_page_info_
.reset(
717 new ErrorPageInfo(committed_error_page_info_
->error
,
718 committed_error_page_info_
->was_failed_post
));
719 pending_error_page_info_
->navigation_correction_response
=
720 ParseNavigationCorrectionResponse(corrections
);
722 std::string error_html
;
723 scoped_ptr
<LocalizedError::ErrorPageParams
> params
;
724 if (pending_error_page_info_
->navigation_correction_response
) {
725 // Copy navigation correction parameters used for the request, so tracking
726 // requests can still be sent if the configuration changes.
727 pending_error_page_info_
->navigation_correction_params
.reset(
728 new NavigationCorrectionParams(
729 *committed_error_page_info_
->navigation_correction_params
));
730 params
= CreateErrorPageParams(
731 *pending_error_page_info_
->navigation_correction_response
,
732 pending_error_page_info_
->error
,
733 *pending_error_page_info_
->navigation_correction_params
,
734 accept_languages
, is_rtl
);
735 delegate_
->GenerateLocalizedErrorPage(
736 pending_error_page_info_
->error
,
737 pending_error_page_info_
->was_failed_post
,
739 &pending_error_page_info_
->reload_button_in_page
,
740 &pending_error_page_info_
->load_stale_button_in_page
,
743 // Since |navigation_correction_params| in |pending_error_page_info_| is
744 // NULL, this won't trigger another attempt to load corrections.
745 GetErrorHtmlForMainFrame(pending_error_page_info_
.get(), &error_html
);
748 // TODO(mmenke): Once the new API is in place, look into replacing this
749 // double page load by just updating the error page, like DNS
751 delegate_
->LoadErrorPageInMainFrame(
753 pending_error_page_info_
->error
.unreachableURL
);
756 blink::WebURLError
NetErrorHelperCore::GetUpdatedError(
757 const blink::WebURLError
& error
) const {
758 // If a probe didn't run or wasn't conclusive, restore the original error.
759 if (last_probe_status_
== chrome_common_net::DNS_PROBE_NOT_RUN
||
760 last_probe_status_
==
761 chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE
) {
765 blink::WebURLError updated_error
;
766 updated_error
.domain
= blink::WebString::fromUTF8(
767 chrome_common_net::kDnsProbeErrorDomain
);
768 updated_error
.reason
= last_probe_status_
;
769 updated_error
.unreachableURL
= error
.unreachableURL
;
770 updated_error
.staleCopyInCache
= error
.staleCopyInCache
;
772 return updated_error
;
775 void NetErrorHelperCore::Reload() {
776 if (!committed_error_page_info_
) {
779 delegate_
->ReloadPage();
782 bool NetErrorHelperCore::MaybeStartAutoReloadTimer() {
783 if (!committed_error_page_info_
||
784 !committed_error_page_info_
->is_finished_loading
||
785 pending_error_page_info_
||
786 uncommitted_load_started_
) {
790 StartAutoReloadTimer();
794 void NetErrorHelperCore::StartAutoReloadTimer() {
795 DCHECK(committed_error_page_info_
);
796 DCHECK(IsReloadableError(*committed_error_page_info_
));
798 committed_error_page_info_
->auto_reload_triggered
= true;
800 if (!online_
|| (!visible_
&& auto_reload_visible_only_
)) {
801 auto_reload_paused_
= true;
805 auto_reload_paused_
= false;
806 base::TimeDelta delay
= GetAutoReloadTime(auto_reload_count_
);
807 auto_reload_timer_
->Stop();
808 auto_reload_timer_
->Start(FROM_HERE
, delay
,
809 base::Bind(&NetErrorHelperCore::AutoReloadTimerFired
,
810 base::Unretained(this)));
813 void NetErrorHelperCore::AutoReloadTimerFired() {
814 auto_reload_count_
++;
818 void NetErrorHelperCore::PauseAutoReloadTimer() {
819 if (!auto_reload_timer_
->IsRunning())
821 DCHECK(committed_error_page_info_
);
822 DCHECK(!auto_reload_paused_
);
823 DCHECK(committed_error_page_info_
->auto_reload_triggered
);
824 auto_reload_timer_
->Stop();
825 auto_reload_paused_
= true;
828 void NetErrorHelperCore::NetworkStateChanged(bool online
) {
829 bool was_online
= online_
;
831 if (!was_online
&& online
) {
832 // Transitioning offline -> online
833 if (auto_reload_paused_
)
834 MaybeStartAutoReloadTimer();
835 } else if (was_online
&& !online
) {
836 // Transitioning online -> offline
837 if (auto_reload_timer_
->IsRunning())
838 auto_reload_count_
= 0;
839 PauseAutoReloadTimer();
843 bool NetErrorHelperCore::ShouldSuppressErrorPage(FrameType frame_type
,
845 // Don't suppress child frame errors.
846 if (frame_type
!= MAIN_FRAME
)
849 if (!auto_reload_enabled_
)
852 // If there's no committed error page, this error page wasn't from an auto
854 if (!committed_error_page_info_
)
857 // If the error page wasn't reloadable, display it.
858 if (!IsReloadableError(*committed_error_page_info_
))
861 // If |auto_reload_timer_| is still running or is paused, this error page
862 // isn't from an auto reload.
863 if (auto_reload_timer_
->IsRunning() || auto_reload_paused_
)
866 // If the error page was reloadable, and the timer isn't running or paused, an
867 // auto-reload has already been triggered.
868 DCHECK(committed_error_page_info_
->auto_reload_triggered
);
870 GURL error_url
= committed_error_page_info_
->error
.unreachableURL
;
871 // TODO(ellyjones): also plumb the error code down to CCRC and check that
872 if (error_url
!= url
)
875 // Suppressed an error-page load; the previous uncommitted load was the error
876 // page load starting, so forget about it.
877 uncommitted_load_started_
= false;
879 // The first iteration of the timer is started by OnFinishLoad calling
880 // MaybeStartAutoReloadTimer, but since error pages for subsequent loads are
881 // suppressed in this function, subsequent iterations of the timer have to be
883 MaybeStartAutoReloadTimer();
887 void NetErrorHelperCore::ExecuteButtonPress(Button button
) {
890 chrome_common_net::RecordEvent(
891 chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_CLICKED
);
892 navigation_from_button_
= RELOAD_BUTTON
;
895 case LOAD_STALE_BUTTON
:
896 chrome_common_net::RecordEvent(
897 chrome_common_net::NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_CLICKED
);
898 navigation_from_button_
= LOAD_STALE_BUTTON
;
899 delegate_
->LoadPageFromCache(
900 committed_error_page_info_
->error
.unreachableURL
);
903 // Visual effects on page are handled in Javascript code.
904 chrome_common_net::RecordEvent(
905 chrome_common_net::NETWORK_ERROR_PAGE_MORE_BUTTON_CLICKED
);
913 void NetErrorHelperCore::TrackClick(int tracking_id
) {
914 // It's technically possible for |navigation_correction_params| to be NULL but
915 // for |navigation_correction_response| not to be NULL, if the paramters
916 // changed between loading the original error page and loading the error page
917 if (!committed_error_page_info_
||
918 !committed_error_page_info_
->navigation_correction_response
) {
922 NavigationCorrectionResponse
* response
=
923 committed_error_page_info_
->navigation_correction_response
.get();
925 // |tracking_id| is less than 0 when the error page was not generated by the
926 // navigation correction service. |tracking_id| should never be greater than
927 // the array size, but best to be safe, since it contains data from a remote
928 // site, though none of that data should make it into Javascript callbacks.
929 if (tracking_id
< 0 ||
930 static_cast<size_t>(tracking_id
) >= response
->corrections
.size()) {
934 // Only report a clicked link once.
935 if (committed_error_page_info_
->clicked_corrections
.count(tracking_id
))
938 committed_error_page_info_
->clicked_corrections
.insert(tracking_id
);
939 std::string request_body
= CreateClickTrackingUrlRequestBody(
940 committed_error_page_info_
->error
,
941 *committed_error_page_info_
->navigation_correction_params
,
943 *response
->corrections
[tracking_id
]);
944 delegate_
->SendTrackingRequest(
945 committed_error_page_info_
->navigation_correction_params
->url
,