d888a1afcc7382268fc6115989ae66a529f3b467
[platform/framework/web/crosswalk.git] / src / chrome / renderer / net / net_error_helper_core.cc
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.
4
5 #include "chrome/renderer/net/net_error_helper_core.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/i18n/rtl.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/location.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string16.h"
17 #include "base/values.h"
18 #include "chrome/common/localized_error.h"
19 #include "grit/generated_resources.h"
20 #include "net/base/escape.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/net_util.h"
23 #include "third_party/WebKit/public/platform/WebString.h"
24 #include "third_party/WebKit/public/platform/WebURLError.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "url/gurl.h"
27
28 namespace {
29
30 struct CorrectionTypeToResourceTable {
31   int resource_id;
32   const char* correction_type;
33 };
34
35 const CorrectionTypeToResourceTable kCorrectionResourceTable[] = {
36   {IDS_ERRORPAGES_SUGGESTION_VISIT_GOOGLE_CACHE, "cachedPage"},
37   // "reloadPage" is has special handling.
38   {IDS_ERRORPAGES_SUGGESTION_CORRECTED_URL, "urlCorrection"},
39   {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "siteDomain"},
40   {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "host"},
41   {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "sitemap"},
42   {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "pathParentFolder"},
43   // "siteSearchQuery" is not yet supported.
44   // TODO(mmenke):  Figure out what format "siteSearchQuery" uses for its
45   // suggestions.
46   // "webSearchQuery" has special handling.
47   {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "contentOverlap"},
48   {IDS_ERRORPAGES_SUGGESTION_CORRECTED_URL, "emphasizedUrlCorrection"},
49 };
50
51 base::TimeDelta GetAutoReloadTime(size_t reload_count) {
52   static const int kDelaysMs[] = {
53     0, 5000, 30000, 60000, 300000, 600000, 1800000
54   };
55   if (reload_count >= arraysize(kDelaysMs))
56     reload_count = arraysize(kDelaysMs) - 1;
57   return base::TimeDelta::FromMilliseconds(kDelaysMs[reload_count]);
58 }
59
60 // Returns whether |net_error| is a DNS-related error (and therefore whether
61 // the tab helper should start a DNS probe after receiving it.)
62 bool IsDnsError(const blink::WebURLError& error) {
63   return error.domain.utf8() == net::kErrorDomain &&
64          (error.reason == net::ERR_NAME_NOT_RESOLVED ||
65           error.reason == net::ERR_NAME_RESOLUTION_FAILED);
66 }
67
68 GURL SanitizeURL(const GURL& url) {
69   GURL::Replacements remove_params;
70   remove_params.ClearUsername();
71   remove_params.ClearPassword();
72   remove_params.ClearQuery();
73   remove_params.ClearRef();
74   return url.ReplaceComponents(remove_params);
75 }
76
77 // If URL correction information should be retrieved remotely for a main frame
78 // load that failed with |error|, returns true and sets
79 // |correction_request_body| to be the body for the correction request.
80 bool GetFixUrlRequestBody(const blink::WebURLError& error,
81                           const std::string& language,
82                           const std::string& country_code,
83                           const std::string& api_key,
84                           std::string* correction_request_body) {
85   // Parameter to send to the correction service indicating the error type.
86   std::string error_param;
87
88   std::string domain = error.domain.utf8();
89   if (domain == "http" && error.reason == 404) {
90     error_param = "http404";
91   } else if (IsDnsError(error)) {
92     error_param = "dnserror";
93   } else if (domain == net::kErrorDomain &&
94              (error.reason == net::ERR_CONNECTION_FAILED ||
95               error.reason == net::ERR_CONNECTION_REFUSED ||
96               error.reason == net::ERR_ADDRESS_UNREACHABLE ||
97               error.reason == net::ERR_CONNECTION_TIMED_OUT)) {
98     error_param = "connectionFailure";
99   } else {
100     return false;
101   }
102
103   // Don't use the correction service for HTTPS (for privacy reasons).
104   GURL unreachable_url(error.unreachableURL);
105   if (unreachable_url.SchemeIsSecure())
106     return false;
107
108   // TODO(yuusuke): Change to net::FormatUrl when Link Doctor becomes
109   // unicode-capable.
110   std::string spec_to_send = SanitizeURL(unreachable_url).spec();
111
112   // Notify navigation correction service of the url truncation by sending of
113   // "?" at the end.
114   if (unreachable_url.has_query())
115     spec_to_send.append("?");
116
117   // Assemble request body, which is a JSON string.
118   // TODO(mmenke):  Investigate open sourcing the relevant protocol buffers and
119   //                using those directly instead.
120
121   base::DictionaryValue request_dict;
122   request_dict.SetString("method", "linkdoctor.fixurl.fixurl");
123   request_dict.SetString("apiVersion", "v1");
124
125   base::DictionaryValue* params_dict = new base::DictionaryValue();
126   request_dict.Set("params", params_dict);
127
128   params_dict->SetString("key", api_key);
129   params_dict->SetString("urlQuery", spec_to_send);
130   params_dict->SetString("clientName", "chrome");
131   params_dict->SetString("error", error_param);
132
133   if (!language.empty())
134     params_dict->SetString("language", language);
135
136   if (!country_code.empty())
137     params_dict->SetString("originCountry", country_code);
138
139   base::JSONWriter::Write(&request_dict, correction_request_body);
140   return true;
141 }
142
143 base::string16 FormatURLForDisplay(const GURL& url, bool is_rtl,
144                                    const std::string accept_languages) {
145   // Translate punycode into UTF8, unescape UTF8 URLs.
146   base::string16 url_for_display(net::FormatUrl(
147       url, accept_languages, net::kFormatUrlOmitNothing,
148       net::UnescapeRule::NORMAL, NULL, NULL, NULL));
149   // URLs are always LTR.
150   if (is_rtl)
151     base::i18n::WrapStringWithLTRFormatting(&url_for_display);
152   return url_for_display;
153 }
154
155 LocalizedError::ErrorPageParams* ParseAdditionalSuggestions(
156     const std::string& data,
157     const GURL& original_url,
158     const GURL& search_url,
159     const std::string& accept_languages,
160     bool is_rtl) {
161   scoped_ptr<base::Value> parsed(base::JSONReader::Read(data));
162   if (!parsed)
163     return NULL;
164   // TODO(mmenke):  Open source related protocol buffers and use them directly.
165   base::DictionaryValue* parsed_dict;
166   base::ListValue* corrections;
167   if (!parsed->GetAsDictionary(&parsed_dict))
168     return NULL;
169   if (!parsed_dict->GetList("result.UrlCorrections", &corrections))
170     return NULL;
171
172   // Version of URL for display in suggestions.  It has to be sanitized first
173   // because any received suggestions will be relative to the sanitized URL.
174   base::string16 original_url_for_display =
175       FormatURLForDisplay(SanitizeURL(original_url), is_rtl, accept_languages);
176
177   scoped_ptr<LocalizedError::ErrorPageParams> params(
178       new LocalizedError::ErrorPageParams());
179   params->override_suggestions.reset(new base::ListValue());
180   scoped_ptr<base::ListValue> parsed_corrections(new base::ListValue());
181   for (base::ListValue::iterator it = corrections->begin();
182        it != corrections->end(); ++it) {
183     base::DictionaryValue* correction;
184     if (!(*it)->GetAsDictionary(&correction))
185       continue;
186
187     // Doesn't seem like a good idea to show these.
188     bool is_porn;
189     if (correction->GetBoolean("isPorn", &is_porn) && is_porn)
190       continue;
191     if (correction->GetBoolean("isSoftPorn", &is_porn) && is_porn)
192       continue;
193
194     std::string correction_type;
195     std::string url_correction;
196     if (!correction->GetString("correctionType", &correction_type) ||
197         !correction->GetString("urlCorrection", &url_correction)) {
198       continue;
199     }
200
201     std::string click_tracking_url;
202     correction->GetString("clickTrackingUrl", &click_tracking_url);
203
204     if (correction_type == "reloadPage") {
205       params->suggest_reload = true;
206       continue;
207     }
208
209     if (correction_type == "webSearchQuery") {
210       // If there are mutliple searches suggested, use the first suggestion.
211       if (params->search_terms.empty()) {
212         params->search_url = search_url;
213         params->search_terms = url_correction;
214       }
215       continue;
216     }
217
218     size_t correction_index;
219     for (correction_index = 0;
220          correction_index < arraysize(kCorrectionResourceTable);
221          ++correction_index) {
222       if (correction_type !=
223               kCorrectionResourceTable[correction_index].correction_type) {
224         continue;
225       }
226       base::DictionaryValue* suggest = new base::DictionaryValue();
227       suggest->SetString("header",
228           l10n_util::GetStringUTF16(
229               kCorrectionResourceTable[correction_index].resource_id));
230       suggest->SetString("urlCorrection",
231           !click_tracking_url.empty() ? click_tracking_url :
232                                         url_correction);
233       suggest->SetString(
234           "urlCorrectionForDisplay",
235           FormatURLForDisplay(GURL(url_correction), is_rtl, accept_languages));
236       suggest->SetString("originalUrlForDisplay", original_url_for_display);
237       params->override_suggestions->Append(suggest);
238       break;
239     }
240   }
241
242   if (params->override_suggestions->empty() &&
243       !params->search_url.is_valid()) {
244     return NULL;
245   }
246   return params.release();
247 }
248
249 }  // namespace
250
251 struct NetErrorHelperCore::ErrorPageInfo {
252   ErrorPageInfo(blink::WebURLError error, bool was_failed_post)
253       : error(error),
254         was_failed_post(was_failed_post),
255         needs_dns_updates(false),
256         is_finished_loading(false) {
257   }
258
259   // Information about the failed page load.
260   blink::WebURLError error;
261   bool was_failed_post;
262
263   // Information about the status of the error page.
264
265   // True if a page is a DNS error page and has not yet received a final DNS
266   // probe status.
267   bool needs_dns_updates;
268
269   // Navigation correction service url, which will be used in response to
270   // certain types of network errors.  This is also stored by the
271   // NetErrorHelperCore itself, but it stored here as well in case its modified
272   // in the middle of an error page load.  Empty when no error page should be
273   // fetched, or if there's already a fetch in progress.
274   GURL navigation_correction_url;
275
276   // Request body to use when requesting corrections from a web service.
277   // TODO(mmenke):  Investigate loading the error page at the same time as
278   //                the blank page is loading, to get rid of these.
279   std::string navigation_correction_request_body;
280
281   // True if a page has completed loading, at which point it can receive
282   // updates.
283   bool is_finished_loading;
284 };
285
286 bool NetErrorHelperCore::IsReloadableError(
287     const NetErrorHelperCore::ErrorPageInfo& info) {
288   return info.error.domain.utf8() == net::kErrorDomain &&
289          info.error.reason != net::ERR_ABORTED &&
290          !info.was_failed_post;
291 }
292
293 NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate)
294     : delegate_(delegate),
295       last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
296       auto_reload_enabled_(false),
297       auto_reload_timer_(new base::Timer(false, false)),
298       // TODO(ellyjones): Make online_ accurate at object creation.
299       online_(true),
300       auto_reload_count_(0),
301       can_auto_reload_page_(false) {
302 }
303
304 NetErrorHelperCore::~NetErrorHelperCore() {
305   if (committed_error_page_info_ && can_auto_reload_page_) {
306     UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop",
307                                      -committed_error_page_info_->error.reason,
308                                      net::GetAllErrorCodesForUma());
309     UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop", auto_reload_count_);
310   }
311 }
312
313 void NetErrorHelperCore::CancelPendingFetches() {
314   // Cancel loading the alternate error page, and prevent any pending error page
315   // load from starting a new error page load.  Swapping in the error page when
316   // it's finished loading could abort the navigation, otherwise.
317   if (committed_error_page_info_ && can_auto_reload_page_) {
318     UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop",
319                                      -committed_error_page_info_->error.reason,
320                                      net::GetAllErrorCodesForUma());
321     UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop", auto_reload_count_);
322   }
323   if (committed_error_page_info_) {
324     committed_error_page_info_->navigation_correction_url = GURL();
325     committed_error_page_info_->navigation_correction_request_body.clear();
326   }
327   if (pending_error_page_info_) {
328     pending_error_page_info_->navigation_correction_url = GURL();
329     pending_error_page_info_->navigation_correction_request_body.clear();
330   }
331   delegate_->CancelFetchNavigationCorrections();
332   auto_reload_timer_->Stop();
333   can_auto_reload_page_ = false;
334 }
335
336 void NetErrorHelperCore::OnStop() {
337   CancelPendingFetches();
338   auto_reload_count_ = 0;
339 }
340
341 void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) {
342   if (frame_type != MAIN_FRAME)
343     return;
344
345   // If there's no pending error page information associated with the page load,
346   // or the new page is not an error page, then reset pending error page state.
347   if (!pending_error_page_info_ || page_type != ERROR_PAGE) {
348     CancelPendingFetches();
349   } else {
350     // If an error load is starting, the resulting error page is autoreloadable.
351     can_auto_reload_page_ = IsReloadableError(*pending_error_page_info_);
352   }
353 }
354
355 void NetErrorHelperCore::OnCommitLoad(FrameType frame_type) {
356   if (frame_type != MAIN_FRAME)
357     return;
358
359   if (committed_error_page_info_ && !pending_error_page_info_ &&
360       can_auto_reload_page_) {
361     int reason = committed_error_page_info_->error.reason;
362     UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess",
363                                      -reason,
364                                      net::GetAllErrorCodesForUma());
365     UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess", auto_reload_count_);
366     if (auto_reload_count_ == 1) {
367       UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess",
368                                        -reason,
369                                        net::GetAllErrorCodesForUma());
370     }
371   }
372
373   committed_error_page_info_.reset(pending_error_page_info_.release());
374 }
375
376 void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) {
377   if (frame_type != MAIN_FRAME)
378     return;
379
380   if (!committed_error_page_info_) {
381     auto_reload_count_ = 0;
382     return;
383   }
384
385   committed_error_page_info_->is_finished_loading = true;
386
387   // Only enable stale cache JS bindings if this wasn't a post.
388   if (!committed_error_page_info_->was_failed_post) {
389     delegate_->EnableStaleLoadBindings(
390         committed_error_page_info_->error.unreachableURL);
391   }
392
393   if (committed_error_page_info_->navigation_correction_url.is_valid()) {
394     // If there is another pending error page load, |fix_url| should have been
395     // cleared.
396     DCHECK(!pending_error_page_info_);
397     DCHECK(!committed_error_page_info_->needs_dns_updates);
398     delegate_->FetchNavigationCorrections(
399         committed_error_page_info_->navigation_correction_url,
400         committed_error_page_info_->navigation_correction_request_body);
401   } else if (auto_reload_enabled_ &&
402              IsReloadableError(*committed_error_page_info_)) {
403     MaybeStartAutoReloadTimer();
404   }
405
406   if (!committed_error_page_info_->needs_dns_updates ||
407       last_probe_status_ == chrome_common_net::DNS_PROBE_POSSIBLE) {
408     return;
409   }
410   DVLOG(1) << "Error page finished loading; sending saved status.";
411   UpdateErrorPage();
412 }
413
414 void NetErrorHelperCore::GetErrorHTML(
415     FrameType frame_type,
416     const blink::WebURLError& error,
417     bool is_failed_post,
418     std::string* error_html) {
419   if (frame_type == MAIN_FRAME) {
420     // If navigation corrections were needed before, that should have been
421     // cancelled earlier by starting a new page load (Which has now failed).
422     DCHECK(!committed_error_page_info_ ||
423            !committed_error_page_info_->navigation_correction_url.is_valid());
424
425     std::string navigation_correction_request_body;
426
427     if (navigation_correction_url_.is_valid() &&
428         GetFixUrlRequestBody(error, language_, country_code_, api_key_,
429                              &navigation_correction_request_body)) {
430       pending_error_page_info_.reset(new ErrorPageInfo(error, is_failed_post));
431       pending_error_page_info_->navigation_correction_url =
432           navigation_correction_url_;
433       pending_error_page_info_->navigation_correction_request_body =
434           navigation_correction_request_body;
435       return;
436     }
437
438     // The last probe status needs to be reset if this is a DNS error.  This
439     // means that if a DNS error page is committed but has not yet finished
440     // loading, a DNS probe status scheduled to be sent to it may be thrown
441     // out, but since the new error page should trigger a new DNS probe, it
442     // will just get the results for the next page load.
443     if (IsDnsError(error))
444       last_probe_status_ = chrome_common_net::DNS_PROBE_POSSIBLE;
445   }
446
447   GenerateLocalErrorPage(frame_type, error, is_failed_post,
448                          scoped_ptr<LocalizedError::ErrorPageParams>(),
449                          error_html);
450 }
451
452 void NetErrorHelperCore::GenerateLocalErrorPage(
453     FrameType frame_type,
454     const blink::WebURLError& error,
455     bool is_failed_post,
456     scoped_ptr<LocalizedError::ErrorPageParams> params,
457     std::string* error_html) {
458   if (frame_type == MAIN_FRAME) {
459     pending_error_page_info_.reset(new ErrorPageInfo(error, is_failed_post));
460     // Skip DNS logic if suggestions were received from a remote server.
461     if (IsDnsError(error) && !params) {
462       // This is not strictly necessary, but waiting for a new status to be
463       // sent as a result of the DidFinishLoading call keeps the histograms
464       // consistent with older versions of the code, at no real cost.
465       last_probe_status_ = chrome_common_net::DNS_PROBE_POSSIBLE;
466
467       delegate_->GenerateLocalizedErrorPage(
468           GetUpdatedError(error), is_failed_post, params.Pass(),
469           error_html);
470       pending_error_page_info_->needs_dns_updates = true;
471       return;
472     }
473   }
474
475   delegate_->GenerateLocalizedErrorPage(error, is_failed_post,
476                                         params.Pass(), error_html);
477 }
478
479 void NetErrorHelperCore::OnNetErrorInfo(
480     chrome_common_net::DnsProbeStatus status) {
481   DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, status);
482
483   last_probe_status_ = status;
484
485   if (!committed_error_page_info_ ||
486       !committed_error_page_info_->needs_dns_updates ||
487       !committed_error_page_info_->is_finished_loading) {
488     return;
489   }
490
491   UpdateErrorPage();
492 }
493
494 void NetErrorHelperCore::OnSetNavigationCorrectionInfo(
495     const GURL& navigation_correction_url,
496     const std::string& language,
497     const std::string& country_code,
498     const std::string& api_key,
499     const GURL& search_url) {
500   navigation_correction_url_ = navigation_correction_url;
501   language_ = language;
502   country_code_ = country_code;
503   api_key_ = api_key;
504   search_url_ = search_url;
505 }
506
507 void NetErrorHelperCore::UpdateErrorPage() {
508   DCHECK(committed_error_page_info_->needs_dns_updates);
509   DCHECK(committed_error_page_info_->is_finished_loading);
510   DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, last_probe_status_);
511
512   UMA_HISTOGRAM_ENUMERATION("DnsProbe.ErrorPageUpdateStatus",
513                             last_probe_status_,
514                             chrome_common_net::DNS_PROBE_MAX);
515   // Every status other than DNS_PROBE_POSSIBLE and DNS_PROBE_STARTED is a
516   // final status code.  Once one is reached, the page does not need further
517   // updates.
518   if (last_probe_status_ != chrome_common_net::DNS_PROBE_STARTED)
519     committed_error_page_info_->needs_dns_updates = false;
520
521   delegate_->UpdateErrorPage(
522       GetUpdatedError(committed_error_page_info_->error),
523       committed_error_page_info_->was_failed_post);
524 }
525
526 void NetErrorHelperCore::OnNavigationCorrectionsFetched(
527     const std::string& corrections,
528     const std::string& accept_languages,
529     bool is_rtl) {
530   // Loading suggestions only starts when a blank error page finishes loading,
531   // and is cancelled with a new load.
532   DCHECK(!pending_error_page_info_);
533   DCHECK(committed_error_page_info_->is_finished_loading);
534
535   scoped_ptr<LocalizedError::ErrorPageParams> params(
536     ParseAdditionalSuggestions(
537         corrections, GURL(committed_error_page_info_->error.unreachableURL),
538         search_url_, accept_languages, is_rtl));
539   std::string error_html;
540   GenerateLocalErrorPage(MAIN_FRAME,
541                          committed_error_page_info_->error,
542                          committed_error_page_info_->was_failed_post,
543                          params.Pass(),
544                          &error_html);
545
546   // |error_page_info| may have been destroyed by this point, since
547   // |pending_error_page_info_| was set to a new ErrorPageInfo.
548
549   // TODO(mmenke):  Once the new API is in place, look into replacing this
550   //                double page load by just updating the error page, like DNS
551   //                probes do.
552   delegate_->LoadErrorPageInMainFrame(
553       error_html,
554       pending_error_page_info_->error.unreachableURL);
555 }
556
557 blink::WebURLError NetErrorHelperCore::GetUpdatedError(
558     const blink::WebURLError& error) const {
559   // If a probe didn't run or wasn't conclusive, restore the original error.
560   if (last_probe_status_ == chrome_common_net::DNS_PROBE_NOT_RUN ||
561       last_probe_status_ ==
562           chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE) {
563     return error;
564   }
565
566   blink::WebURLError updated_error;
567   updated_error.domain = blink::WebString::fromUTF8(
568       chrome_common_net::kDnsProbeErrorDomain);
569   updated_error.reason = last_probe_status_;
570   updated_error.unreachableURL = error.unreachableURL;
571   updated_error.staleCopyInCache = error.staleCopyInCache;
572
573   return updated_error;
574 }
575
576 void NetErrorHelperCore::Reload() {
577   if (!committed_error_page_info_) {
578     return;
579   }
580   delegate_->ReloadPage();
581 }
582
583 bool NetErrorHelperCore::MaybeStartAutoReloadTimer() {
584   if (!committed_error_page_info_ ||
585       !committed_error_page_info_->is_finished_loading ||
586       !can_auto_reload_page_ ||
587       pending_error_page_info_) {
588     return false;
589   }
590
591   DCHECK(IsReloadableError(*committed_error_page_info_));
592
593   if (!online_)
594     return false;
595
596   StartAutoReloadTimer();
597   return true;
598 }
599
600 void NetErrorHelperCore::StartAutoReloadTimer() {
601   DCHECK(committed_error_page_info_);
602   DCHECK(can_auto_reload_page_);
603   base::TimeDelta delay = GetAutoReloadTime(auto_reload_count_);
604   auto_reload_count_++;
605   auto_reload_timer_->Stop();
606   auto_reload_timer_->Start(FROM_HERE, delay,
607       base::Bind(&NetErrorHelperCore::Reload,
608                  base::Unretained(this)));
609 }
610
611 void NetErrorHelperCore::NetworkStateChanged(bool online) {
612   online_ = online;
613   if (auto_reload_timer_->IsRunning()) {
614     DCHECK(committed_error_page_info_);
615     // If there's an existing timer running, stop it and reset the retry count.
616     auto_reload_timer_->Stop();
617     auto_reload_count_ = 0;
618   }
619
620   // If the network state changed to online, maybe start auto-reloading again.
621   if (online)
622     MaybeStartAutoReloadTimer();
623 }
624
625 bool NetErrorHelperCore::ShouldSuppressErrorPage(FrameType frame_type,
626                                                  const GURL& url) {
627   // Don't suppress child frame errors.
628   if (frame_type != MAIN_FRAME)
629     return false;
630
631   // If |auto_reload_timer_| is still running, this error page isn't from an
632   // auto reload.
633   if (auto_reload_timer_->IsRunning())
634     return false;
635
636   // If there's no committed error page, this error page wasn't from an auto
637   // reload.
638   if (!committed_error_page_info_ || !can_auto_reload_page_)
639     return false;
640
641   GURL error_url = committed_error_page_info_->error.unreachableURL;
642   // TODO(ellyjones): also plumb the error code down to CCRC and check that
643   if (error_url != url)
644     return false;
645
646   // The first iteration of the timer is started by OnFinishLoad calling
647   // MaybeStartAutoReloadTimer, but since error pages for subsequent loads are
648   // suppressed in this function, subsequent iterations of the timer have to be
649   // started here.
650   MaybeStartAutoReloadTimer();
651   return true;
652 }