Revert "Fix for missing change in the revert of default surprise me behaviour."
[chromium-blink-merge.git] / chrome / renderer / net / net_error_helper_core.cc
blob94196295869d1bc75ac517632ed30728a763b4e6
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"
7 #include <set>
8 #include <string>
9 #include <vector>
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"
33 #include "url/gurl.h"
35 namespace {
37 struct CorrectionTypeToResourceTable {
38 int resource_id;
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
52 // suggestions.
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;
81 bool is_porn;
82 bool is_soft_porn;
85 struct NavigationCorrectionResponse {
86 std::string event_id;
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
131 // unicode-capable.
132 std::string spec_to_send = SanitizeURL(url).spec();
134 // Notify navigation correction service of the url truncation by sending of
135 // "?" at the end.
136 if (url.has_query())
137 spec_to_send.append("?");
138 return spec_to_send;
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())
151 return false;
153 std::string domain = error.domain.utf8();
154 if (domain == "http" && error.reason == 404) {
155 *error_param = "http404";
156 return true;
158 if (IsDnsError(error)) {
159 *error_param = "dnserror";
160 return true;
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";
168 return true;
170 return false;
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);
199 DCHECK(success);
200 return 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);
211 DCHECK(result);
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);
228 DCHECK(result);
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.
253 if (is_rtl)
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()))
266 response.reset();
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,
275 bool is_rtl) {
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,
280 accept_languages);
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)
291 continue;
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;
298 continue;
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;
308 continue;
311 // Allow reload page and web search query to be empty strings, but not
312 // links.
313 if ((*it)->url_correction.empty())
314 continue;
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) {
321 continue;
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);
328 suggest->SetString(
329 "urlCorrectionForDisplay",
330 FormatURLForDisplay(GURL((*it)->url_correction), is_rtl,
331 accept_languages));
332 suggest->SetString("originalUrlForDisplay", original_url_for_display);
333 suggest->SetInteger("trackingId", tracking_id);
334 params->override_suggestions->Append(suggest);
335 break;
339 if (params->override_suggestions->empty() && !params->search_url.is_valid())
340 params.reset();
341 return params.Pass();
344 void ReportAutoReloadSuccess(const blink::WebURLError& error, size_t count) {
345 if (error.domain.utf8() != net::kErrorDomain)
346 return;
347 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess",
348 -error.reason,
349 net::GetAllErrorCodesForUma());
350 UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess", count);
351 if (count == 1) {
352 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess",
353 -error.reason,
354 net::GetAllErrorCodesForUma());
358 void ReportAutoReloadFailure(const blink::WebURLError& error, size_t count) {
359 if (error.domain.utf8() != net::kErrorDomain)
360 return;
361 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop",
362 -error.reason,
363 net::GetAllErrorCodesForUma());
364 UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop", count);
367 } // namespace
369 struct NetErrorHelperCore::ErrorPageInfo {
370 ErrorPageInfo(blink::WebURLError error, bool was_failed_post)
371 : error(error),
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
388 // probe status.
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
404 // purposes.
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
412 // updates.
413 bool is_finished_loading;
415 // True if the auto-reload timer has fired and a reload is or has been in
416 // flight.
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,
436 bool is_visible)
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.
445 online_(true),
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,
455 auto_reload_count_);
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,
476 auto_reload_count_);
478 CancelPendingFetches();
479 uncommitted_load_started_ = false;
480 auto_reload_count_ = 0;
483 void NetErrorHelperCore::OnWasShown() {
484 visible_ = true;
485 if (!auto_reload_visible_only_)
486 return;
487 if (auto_reload_paused_)
488 MaybeStartAutoReloadTimer();
491 void NetErrorHelperCore::OnWasHidden() {
492 visible_ = false;
493 if (!auto_reload_visible_only_)
494 return;
495 PauseAutoReloadTimer();
498 void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) {
499 if (frame_type != MAIN_FRAME)
500 return;
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)
512 return;
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)
552 return;
554 if (!committed_error_page_info_) {
555 auto_reload_count_ = 0;
556 return;
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
575 // cleared.
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) {
590 return;
592 DVLOG(1) << "Error page finished loading; sending saved status.";
593 UpdateErrorPage();
596 void NetErrorHelperCore::GetErrorHTML(
597 FrameType frame_type,
598 const blink::WebURLError& error,
599 bool is_failed_post,
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);
611 } else {
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,
619 error_html);
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) {
632 return;
635 UpdateErrorPage();
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;
661 return;
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,
680 error_html);
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",
689 last_probe_status_,
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
693 // updates.
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,
708 bool is_rtl) {
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,
738 params.Pass(),
739 &pending_error_page_info_->reload_button_in_page,
740 &pending_error_page_info_->load_stale_button_in_page,
741 &error_html);
742 } else {
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
750 // probes do.
751 delegate_->LoadErrorPageInMainFrame(
752 error_html,
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) {
762 return error;
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_) {
777 return;
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_) {
787 return false;
790 StartAutoReloadTimer();
791 return true;
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;
802 return;
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_++;
815 Reload();
818 void NetErrorHelperCore::PauseAutoReloadTimer() {
819 if (!auto_reload_timer_->IsRunning())
820 return;
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_;
830 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,
844 const GURL& url) {
845 // Don't suppress child frame errors.
846 if (frame_type != MAIN_FRAME)
847 return false;
849 if (!auto_reload_enabled_)
850 return false;
852 // If there's no committed error page, this error page wasn't from an auto
853 // reload.
854 if (!committed_error_page_info_)
855 return false;
857 // If the error page wasn't reloadable, display it.
858 if (!IsReloadableError(*committed_error_page_info_))
859 return false;
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_)
864 return false;
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)
873 return false;
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
882 // started here.
883 MaybeStartAutoReloadTimer();
884 return true;
887 void NetErrorHelperCore::ExecuteButtonPress(Button button) {
888 switch (button) {
889 case RELOAD_BUTTON:
890 chrome_common_net::RecordEvent(
891 chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_CLICKED);
892 navigation_from_button_ = RELOAD_BUTTON;
893 Reload();
894 return;
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);
901 return;
902 case MORE_BUTTON:
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);
906 return;
907 case NO_BUTTON:
908 NOTREACHED();
909 return;
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) {
919 return;
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()) {
931 return;
934 // Only report a clicked link once.
935 if (committed_error_page_info_->clicked_corrections.count(tracking_id))
936 return;
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,
942 *response,
943 *response->corrections[tracking_id]);
944 delegate_->SendTrackingRequest(
945 committed_error_page_info_->navigation_correction_params->url,
946 request_body);