1// Copyright (c) 2012 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/common/localized_error.h"
6
7#include "base/i18n/rtl.h"
8#include "base/logging.h"
9#include "base/strings/string16.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "chrome/common/net/net_error_info.h"
15#include "chrome/grit/chromium_strings.h"
16#include "chrome/grit/generated_resources.h"
17#include "extensions/common/constants.h"
18#include "extensions/common/extension_icon_set.h"
19#include "extensions/common/manifest_handlers/icons_handler.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/WebURLError.h"
24#include "ui/base/l10n/l10n_util.h"
25#include "ui/base/webui/web_ui_util.h"
26
27#if defined(OS_WIN)
28#include "base/win/windows_version.h"
29#endif
30
31using blink::WebURLError;
32
33// Some error pages have no details.
34const unsigned int kErrorPagesNoDetails = 0;
35
36namespace {
37
38static const char kRedirectLoopLearnMoreUrl[] =
39    "https://www.google.com/support/chrome/bin/answer.py?answer=95626";
40static const char kWeakDHKeyLearnMoreUrl[] =
41    "http://sites.google.com/a/chromium.org/dev/"
42    "err_ssl_weak_server_ephemeral_dh_key";
43#if defined(OS_CHROMEOS)
44static const char kAppWarningLearnMoreUrl[] =
45    "chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html"
46    "?answer=1721911";
47#endif  // defined(OS_CHROMEOS)
48
49enum NAV_SUGGESTIONS {
50  SUGGEST_NONE                  = 0,
51  SUGGEST_RELOAD                = 1 << 0,
52  SUGGEST_CHECK_CONNECTION      = 1 << 1,
53  SUGGEST_DNS_CONFIG            = 1 << 2,
54  SUGGEST_FIREWALL_CONFIG       = 1 << 3,
55  SUGGEST_PROXY_CONFIG          = 1 << 4,
56  SUGGEST_DISABLE_EXTENSION     = 1 << 5,
57  SUGGEST_LEARNMORE             = 1 << 6,
58  SUGGEST_VIEW_POLICIES         = 1 << 7,
59  SUGGEST_CONTACT_ADMINISTRATOR = 1 << 8,
60};
61
62struct LocalizedErrorMap {
63  int error_code;
64  unsigned int title_resource_id;
65  unsigned int heading_resource_id;
66  // Detailed summary used when the error is in the main frame.
67  unsigned int summary_resource_id;
68  // Short one sentence description shown on mouse over when the error is in
69  // a frame.
70  unsigned int details_resource_id;
71  int suggestions;  // Bitmap of SUGGEST_* values.
72};
73
74const LocalizedErrorMap net_error_options[] = {
75  {net::ERR_TIMED_OUT,
76   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
77   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
78   IDS_ERRORPAGES_SUMMARY_TIMED_OUT,
79   IDS_ERRORPAGES_DETAILS_TIMED_OUT,
80   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG |
81       SUGGEST_PROXY_CONFIG,
82  },
83  {net::ERR_CONNECTION_TIMED_OUT,
84   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
85   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
86   IDS_ERRORPAGES_SUMMARY_TIMED_OUT,
87   IDS_ERRORPAGES_DETAILS_TIMED_OUT,
88   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG |
89       SUGGEST_PROXY_CONFIG,
90  },
91  {net::ERR_CONNECTION_CLOSED,
92   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
93   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
94   IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE,
95   IDS_ERRORPAGES_DETAILS_CONNECTION_CLOSED,
96   SUGGEST_RELOAD,
97  },
98  {net::ERR_CONNECTION_RESET,
99   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
100   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
101   IDS_ERRORPAGES_SUMMARY_CONNECTION_RESET,
102   IDS_ERRORPAGES_DETAILS_CONNECTION_RESET,
103   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG |
104       SUGGEST_PROXY_CONFIG,
105  },
106  {net::ERR_CONNECTION_REFUSED,
107   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
108   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
109   IDS_ERRORPAGES_SUMMARY_CONNECTION_REFUSED,
110   IDS_ERRORPAGES_DETAILS_CONNECTION_REFUSED,
111   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG |
112       SUGGEST_PROXY_CONFIG,
113  },
114  {net::ERR_CONNECTION_FAILED,
115   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
116   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
117   IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE,
118   IDS_ERRORPAGES_DETAILS_CONNECTION_FAILED,
119   SUGGEST_RELOAD,
120  },
121  {net::ERR_NAME_NOT_RESOLVED,
122   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
123   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
124   IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED,
125   IDS_ERRORPAGES_DETAILS_NAME_NOT_RESOLVED,
126   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION | SUGGEST_DNS_CONFIG |
127       SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG,
128  },
129  {net::ERR_ADDRESS_UNREACHABLE,
130   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
131   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
132   IDS_ERRORPAGES_SUMMARY_ADDRESS_UNREACHABLE,
133   IDS_ERRORPAGES_DETAILS_ADDRESS_UNREACHABLE,
134   SUGGEST_RELOAD | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG,
135  },
136  {net::ERR_NETWORK_ACCESS_DENIED,
137   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
138   IDS_ERRORPAGES_HEADING_NETWORK_ACCESS_DENIED,
139   IDS_ERRORPAGES_SUMMARY_NETWORK_ACCESS_DENIED,
140   IDS_ERRORPAGES_DETAILS_NETWORK_ACCESS_DENIED,
141   SUGGEST_FIREWALL_CONFIG,
142  },
143  {net::ERR_PROXY_CONNECTION_FAILED,
144   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
145   IDS_ERRORPAGES_HEADING_PROXY_CONNECTION_FAILED,
146   IDS_ERRORPAGES_SUMMARY_PROXY_CONNECTION_FAILED,
147   IDS_ERRORPAGES_DETAILS_PROXY_CONNECTION_FAILED,
148   SUGGEST_PROXY_CONFIG,
149  },
150  {net::ERR_INTERNET_DISCONNECTED,
151   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
152   IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED,
153   IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED,
154   IDS_ERRORPAGES_DETAILS_INTERNET_DISCONNECTED,
155   SUGGEST_NONE,
156  },
157  {net::ERR_FILE_NOT_FOUND,
158   IDS_ERRORPAGES_TITLE_NOT_FOUND,
159   IDS_ERRORPAGES_HEADING_NOT_FOUND,
160   IDS_ERRORPAGES_SUMMARY_NOT_FOUND,
161   IDS_ERRORPAGES_DETAILS_FILE_NOT_FOUND,
162   SUGGEST_NONE,
163  },
164  {net::ERR_CACHE_MISS,
165   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
166   IDS_ERRORPAGES_HEADING_CACHE_MISS,
167   IDS_ERRORPAGES_SUMMARY_CACHE_MISS,
168   IDS_ERRORPAGES_DETAILS_CACHE_MISS,
169   SUGGEST_RELOAD,
170  },
171  {net::ERR_CACHE_READ_FAILURE,
172   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
173   IDS_ERRORPAGES_HEADING_CACHE_READ_FAILURE,
174   IDS_ERRORPAGES_SUMMARY_CACHE_READ_FAILURE,
175   IDS_ERRORPAGES_DETAILS_CACHE_READ_FAILURE,
176   SUGGEST_RELOAD,
177  },
178  {net::ERR_NETWORK_IO_SUSPENDED,
179   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
180   IDS_ERRORPAGES_HEADING_NETWORK_IO_SUSPENDED,
181   IDS_ERRORPAGES_SUMMARY_NETWORK_IO_SUSPENDED,
182   IDS_ERRORPAGES_DETAILS_NETWORK_IO_SUSPENDED,
183   SUGGEST_RELOAD,
184  },
185  {net::ERR_TOO_MANY_REDIRECTS,
186   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
187   IDS_ERRORPAGES_HEADING_TOO_MANY_REDIRECTS,
188   IDS_ERRORPAGES_SUMMARY_TOO_MANY_REDIRECTS,
189   IDS_ERRORPAGES_DETAILS_TOO_MANY_REDIRECTS,
190   SUGGEST_RELOAD | SUGGEST_LEARNMORE,
191  },
192  {net::ERR_EMPTY_RESPONSE,
193   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
194   IDS_ERRORPAGES_HEADING_EMPTY_RESPONSE,
195   IDS_ERRORPAGES_SUMMARY_EMPTY_RESPONSE,
196   IDS_ERRORPAGES_DETAILS_EMPTY_RESPONSE,
197   SUGGEST_RELOAD,
198  },
199  {net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH,
200   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
201   IDS_ERRORPAGES_HEADING_DUPLICATE_HEADERS,
202   IDS_ERRORPAGES_SUMMARY_DUPLICATE_HEADERS,
203   IDS_ERRORPAGES_DETAILS_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH,
204   SUGGEST_NONE,
205  },
206  {net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION,
207   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
208   IDS_ERRORPAGES_HEADING_DUPLICATE_HEADERS,
209   IDS_ERRORPAGES_SUMMARY_DUPLICATE_HEADERS,
210   IDS_ERRORPAGES_DETAILS_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION,
211   SUGGEST_NONE,
212  },
213  {net::ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION,
214   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
215   IDS_ERRORPAGES_HEADING_DUPLICATE_HEADERS,
216   IDS_ERRORPAGES_SUMMARY_DUPLICATE_HEADERS,
217   IDS_ERRORPAGES_DETAILS_RESPONSE_HEADERS_MULTIPLE_LOCATION,
218   SUGGEST_NONE,
219  },
220  {net::ERR_CONTENT_LENGTH_MISMATCH,
221   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
222   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
223   IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE,
224   IDS_ERRORPAGES_DETAILS_CONNECTION_CLOSED,
225   SUGGEST_RELOAD,
226  },
227  {net::ERR_INCOMPLETE_CHUNKED_ENCODING,
228   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
229   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
230   IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE,
231   IDS_ERRORPAGES_DETAILS_CONNECTION_CLOSED,
232   SUGGEST_RELOAD,
233  },
234  {net::ERR_SSL_PROTOCOL_ERROR,
235   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
236   IDS_ERRORPAGES_HEADING_SSL_PROTOCOL_ERROR,
237   IDS_ERRORPAGES_SUMMARY_SSL_PROTOCOL_ERROR,
238   IDS_ERRORPAGES_DETAILS_SSL_PROTOCOL_ERROR,
239   SUGGEST_NONE,
240  },
241  {net::ERR_SSL_UNSAFE_NEGOTIATION,
242   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
243   IDS_ERRORPAGES_HEADING_SSL_PROTOCOL_ERROR,
244   IDS_ERRORPAGES_SUMMARY_SSL_PROTOCOL_ERROR,
245   IDS_ERRORPAGES_DETAILS_SSL_UNSAFE_NEGOTIATION,
246   SUGGEST_NONE,
247  },
248  {net::ERR_BAD_SSL_CLIENT_AUTH_CERT,
249   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
250   IDS_ERRORPAGES_HEADING_BAD_SSL_CLIENT_AUTH_CERT,
251   IDS_ERRORPAGES_SUMMARY_BAD_SSL_CLIENT_AUTH_CERT,
252   IDS_ERRORPAGES_DETAILS_BAD_SSL_CLIENT_AUTH_CERT,
253   SUGGEST_NONE,
254  },
255  {net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY,
256   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
257   IDS_ERRORPAGES_HEADING_WEAK_SERVER_EPHEMERAL_DH_KEY,
258   IDS_ERRORPAGES_SUMMARY_WEAK_SERVER_EPHEMERAL_DH_KEY,
259   IDS_ERRORPAGES_DETAILS_SSL_PROTOCOL_ERROR,
260   SUGGEST_LEARNMORE,
261  },
262  {net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN,
263   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
264   IDS_ERRORPAGES_HEADING_PINNING_FAILURE,
265   IDS_ERRORPAGES_SUMMARY_PINNING_FAILURE,
266   IDS_ERRORPAGES_DETAILS_PINNING_FAILURE,
267   SUGGEST_NONE,
268  },
269  {net::ERR_TEMPORARILY_THROTTLED,
270   IDS_ERRORPAGES_TITLE_ACCESS_DENIED,
271   IDS_ERRORPAGES_HEADING_ACCESS_DENIED,
272   IDS_ERRORPAGES_SUMMARY_TEMPORARILY_THROTTLED,
273   IDS_ERRORPAGES_DETAILS_TEMPORARILY_THROTTLED,
274   SUGGEST_NONE,
275  },
276  {net::ERR_BLOCKED_BY_CLIENT,
277   IDS_ERRORPAGES_TITLE_BLOCKED,
278   IDS_ERRORPAGES_HEADING_BLOCKED,
279   IDS_ERRORPAGES_SUMMARY_BLOCKED,
280   IDS_ERRORPAGES_DETAILS_BLOCKED,
281   SUGGEST_RELOAD | SUGGEST_DISABLE_EXTENSION,
282  },
283  {net::ERR_NETWORK_CHANGED,
284   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
285   IDS_ERRORPAGES_HEADING_NETWORK_ACCESS_DENIED,
286   IDS_ERRORPAGES_SUMMARY_NETWORK_CHANGED,
287   IDS_ERRORPAGES_DETAILS_NETWORK_CHANGED,
288   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION,
289  },
290  {net::ERR_BLOCKED_BY_ADMINISTRATOR,
291   IDS_ERRORPAGES_TITLE_BLOCKED,
292   IDS_ERRORPAGES_HEADING_BLOCKED_BY_ADMINISTRATOR,
293   IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_ADMINISTRATOR,
294   IDS_ERRORPAGES_DETAILS_BLOCKED_BY_ADMINISTRATOR,
295   SUGGEST_VIEW_POLICIES | SUGGEST_CONTACT_ADMINISTRATOR,
296  },
297  {net::ERR_BLOCKED_ENROLLMENT_CHECK_PENDING,
298   IDS_ERRORPAGES_TITLE_BLOCKED,
299   IDS_ERRORPAGES_HEADING_BLOCKED_BY_ADMINISTRATOR,
300   IDS_ERRORPAGES_SUMMARY_BLOCKED_ENROLLMENT_CHECK_PENDING,
301   IDS_ERRORPAGES_DETAILS_BLOCKED_ENROLLMENT_CHECK_PENDING,
302   SUGGEST_CHECK_CONNECTION,
303  },
304};
305
306// Special error page to be used in the case of navigating back to a page
307// generated by a POST.  LocalizedError::HasStrings expects this net error code
308// to also appear in the array above.
309const LocalizedErrorMap repost_error = {
310  net::ERR_CACHE_MISS,
311  IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
312  IDS_HTTP_POST_WARNING_TITLE,
313  IDS_ERRORPAGES_HTTP_POST_WARNING,
314  IDS_ERRORPAGES_DETAILS_CACHE_MISS,
315  SUGGEST_RELOAD,
316};
317
318const LocalizedErrorMap http_error_options[] = {
319  {403,
320   IDS_ERRORPAGES_TITLE_ACCESS_DENIED,
321   IDS_ERRORPAGES_HEADING_ACCESS_DENIED,
322   IDS_ERRORPAGES_SUMMARY_FORBIDDEN,
323   IDS_ERRORPAGES_DETAILS_FORBIDDEN,
324   SUGGEST_NONE,
325  },
326  {410,
327   IDS_ERRORPAGES_TITLE_NOT_FOUND,
328   IDS_ERRORPAGES_HEADING_NOT_FOUND,
329   IDS_ERRORPAGES_SUMMARY_GONE,
330   IDS_ERRORPAGES_DETAILS_GONE,
331   SUGGEST_NONE,
332  },
333
334  {500,
335   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
336   IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR,
337   IDS_ERRORPAGES_SUMMARY_INTERNAL_SERVER_ERROR,
338   IDS_ERRORPAGES_DETAILS_INTERNAL_SERVER_ERROR,
339   SUGGEST_RELOAD,
340  },
341  {501,
342   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
343   IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR,
344   IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE,
345   IDS_ERRORPAGES_DETAILS_NOT_IMPLEMENTED,
346   SUGGEST_NONE,
347  },
348  {502,
349   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
350   IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR,
351   IDS_ERRORPAGES_SUMMARY_BAD_GATEWAY,
352   IDS_ERRORPAGES_DETAILS_BAD_GATEWAY,
353   SUGGEST_RELOAD,
354  },
355  {503,
356   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
357   IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR,
358   IDS_ERRORPAGES_SUMMARY_SERVICE_UNAVAILABLE,
359   IDS_ERRORPAGES_DETAILS_SERVICE_UNAVAILABLE,
360   SUGGEST_RELOAD,
361  },
362  {504,
363   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
364   IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR,
365   IDS_ERRORPAGES_SUMMARY_GATEWAY_TIMEOUT,
366   IDS_ERRORPAGES_DETAILS_GATEWAY_TIMEOUT,
367   SUGGEST_RELOAD,
368  },
369  {505,
370   IDS_ERRORPAGES_TITLE_LOAD_FAILED,
371   IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR,
372   IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE,
373   IDS_ERRORPAGES_DETAILS_HTTP_VERSION_NOT_SUPPORTED,
374   SUGGEST_NONE,
375  },
376};
377
378const LocalizedErrorMap dns_probe_error_options[] = {
379  {chrome_common_net::DNS_PROBE_POSSIBLE,
380   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
381   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
382   IDS_ERRORPAGES_SUMMARY_DNS_PROBE_RUNNING,
383   IDS_ERRORPAGES_DETAILS_DNS_PROBE_RUNNING,
384   SUGGEST_RELOAD,
385  },
386
387  // DNS_PROBE_NOT_RUN is not here; NetErrorHelper will restore the original
388  // error, which might be one of several DNS-related errors.
389
390  {chrome_common_net::DNS_PROBE_STARTED,
391   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
392   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
393   IDS_ERRORPAGES_SUMMARY_DNS_PROBE_RUNNING,
394   IDS_ERRORPAGES_DETAILS_DNS_PROBE_RUNNING,
395   // Include SUGGEST_RELOAD so the More button doesn't jump when we update.
396   SUGGEST_RELOAD,
397  },
398
399  // DNS_PROBE_FINISHED_UNKNOWN is not here; NetErrorHelper will restore the
400  // original error, which might be one of several DNS-related errors.
401
402  {chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
403   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
404   IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED,
405   IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED,
406   IDS_ERRORPAGES_DETAILS_INTERNET_DISCONNECTED,
407   SUGGEST_RELOAD | SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG,
408  },
409  {chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG,
410   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
411   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
412   IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED,
413   IDS_ERRORPAGES_DETAILS_NAME_NOT_RESOLVED,
414   SUGGEST_RELOAD | SUGGEST_DNS_CONFIG | SUGGEST_FIREWALL_CONFIG,
415  },
416  {chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN,
417   IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
418   IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
419   IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED,
420   IDS_ERRORPAGES_DETAILS_NAME_NOT_RESOLVED,
421   SUGGEST_RELOAD,
422  },
423};
424
425const LocalizedErrorMap* FindErrorMapInArray(const LocalizedErrorMap* maps,
426                                                   size_t num_maps,
427                                                   int error_code) {
428  for (size_t i = 0; i < num_maps; ++i) {
429    if (maps[i].error_code == error_code)
430      return &maps[i];
431  }
432  return NULL;
433}
434
435const LocalizedErrorMap* LookupErrorMap(const std::string& error_domain,
436                                        int error_code, bool is_post) {
437  if (error_domain == net::kErrorDomain) {
438    // Display a different page in the special case of navigating through the
439    // history to an uncached page created by a POST.
440    if (is_post && error_code == net::ERR_CACHE_MISS)
441      return &repost_error;
442    return FindErrorMapInArray(net_error_options,
443                               arraysize(net_error_options),
444                               error_code);
445  } else if (error_domain == LocalizedError::kHttpErrorDomain) {
446    return FindErrorMapInArray(http_error_options,
447                               arraysize(http_error_options),
448                               error_code);
449  } else if (error_domain == chrome_common_net::kDnsProbeErrorDomain) {
450    const LocalizedErrorMap* map =
451        FindErrorMapInArray(dns_probe_error_options,
452                            arraysize(dns_probe_error_options),
453                            error_code);
454    DCHECK(map);
455    return map;
456  } else {
457    NOTREACHED();
458    return NULL;
459  }
460}
461
462bool LocaleIsRTL() {
463  return base::i18n::IsRTL();
464}
465
466// Returns a dictionary containing the strings for the settings menu under the
467// wrench, and the advanced settings button.
468base::DictionaryValue* GetStandardMenuItemsText() {
469  base::DictionaryValue* standard_menu_items_text = new base::DictionaryValue();
470  standard_menu_items_text->SetString("settingsTitle",
471      l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
472  standard_menu_items_text->SetString("advancedTitle",
473      l10n_util::GetStringUTF16(IDS_SETTINGS_SHOW_ADVANCED_SETTINGS));
474  return standard_menu_items_text;
475}
476
477// Gets the icon class for a given |error_domain| and |error_code|.
478const char* GetIconClassForError(const std::string& error_domain,
479                                 int error_code) {
480  if ((error_code == net::ERR_INTERNET_DISCONNECTED &&
481       error_domain == net::kErrorDomain) ||
482      (error_code == chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET &&
483       error_domain == chrome_common_net::kDnsProbeErrorDomain))
484    return "icon-offline";
485
486  return "icon-generic";
487}
488
489}  // namespace
490
491const char LocalizedError::kHttpErrorDomain[] = "http";
492
493LocalizedError::ErrorPageParams::ErrorPageParams()
494    : suggest_reload(false),
495      reload_tracking_id(-1),
496      search_tracking_id(-1) {
497}
498
499LocalizedError::ErrorPageParams::~ErrorPageParams() {
500}
501
502void LocalizedError::GetStrings(int error_code,
503                                const std::string& error_domain,
504                                const GURL& failed_url,
505                                bool is_post,
506                                bool show_stale_load_button,
507                                const std::string& locale,
508                                const std::string& accept_languages,
509                                scoped_ptr<ErrorPageParams> params,
510                                base::DictionaryValue* error_strings) {
511  bool rtl = LocaleIsRTL();
512  error_strings->SetString("textdirection", rtl ? "rtl" : "ltr");
513  webui::SetFontAndTextDirection(error_strings);
514
515  // Grab the strings and settings that depend on the error type.  Init
516  // options with default values.
517  LocalizedErrorMap options = {
518    0,
519    IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
520    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
521    IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE,
522    kErrorPagesNoDetails,
523    SUGGEST_NONE,
524  };
525
526  const LocalizedErrorMap* error_map = LookupErrorMap(error_domain, error_code,
527                                                      is_post);
528  if (error_map)
529    options = *error_map;
530
531  // If we got "access denied" but the url was a file URL, then we say it was a
532  // file instead of just using the "not available" default message. Just adding
533  // ERR_ACCESS_DENIED to the map isn't sufficient, since that message may be
534  // generated by some OSs when the operation doesn't involve a file URL.
535  if (error_domain == net::kErrorDomain &&
536      error_code == net::ERR_ACCESS_DENIED &&
537      failed_url.scheme() == "file") {
538    options.title_resource_id = IDS_ERRORPAGES_TITLE_ACCESS_DENIED;
539    options.heading_resource_id = IDS_ERRORPAGES_HEADING_FILE_ACCESS_DENIED;
540    options.summary_resource_id = IDS_ERRORPAGES_SUMMARY_FILE_ACCESS_DENIED;
541    options.details_resource_id = IDS_ERRORPAGES_DETAILS_FILE_ACCESS_DENIED;
542    options.suggestions = SUGGEST_NONE;
543  }
544
545  base::string16 failed_url_string(net::FormatUrl(
546      failed_url, accept_languages, net::kFormatUrlOmitNothing,
547      net::UnescapeRule::NORMAL, NULL, NULL, NULL));
548  // URLs are always LTR.
549  if (rtl)
550    base::i18n::WrapStringWithLTRFormatting(&failed_url_string);
551  error_strings->SetString("title",
552      l10n_util::GetStringFUTF16(options.title_resource_id, failed_url_string));
553  error_strings->SetString("heading",
554      l10n_util::GetStringUTF16(options.heading_resource_id));
555
556  std::string icon_class = GetIconClassForError(error_domain, error_code);
557  error_strings->SetString("iconClass", icon_class);
558
559  base::DictionaryValue* summary = new base::DictionaryValue;
560
561  // For offline show a summary message underneath the heading.
562  if (error_code == net::ERR_INTERNET_DISCONNECTED ||
563      error_code == chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET) {
564    error_strings->SetString("primaryParagraph",
565        l10n_util::GetStringUTF16(options.summary_resource_id));
566  } else {
567    // Set summary message in the details.
568    summary->SetString("msg",
569        l10n_util::GetStringUTF16(options.summary_resource_id));
570  }
571  summary->SetString("failedUrl", failed_url_string);
572  summary->SetString("hostName", net::IDNToUnicode(failed_url.host(),
573                                                   accept_languages));
574  summary->SetString("productName",
575                     l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
576
577  error_strings->SetString(
578      "details", l10n_util::GetStringUTF16(IDS_ERRORPAGE_NET_BUTTON_DETAILS));
579  error_strings->SetString(
580      "hideDetails", l10n_util::GetStringUTF16(
581          IDS_ERRORPAGE_NET_BUTTON_HIDE_DETAILS));
582  error_strings->Set("summary", summary);
583
584  if (options.details_resource_id != kErrorPagesNoDetails) {
585    error_strings->SetString(
586        "errorDetails", l10n_util::GetStringUTF16(options.details_resource_id));
587  }
588
589  base::string16 error_string;
590  if (error_domain == net::kErrorDomain) {
591    // Non-internationalized error string, for debugging Chrome itself.
592    error_string = base::ASCIIToUTF16(net::ErrorToShortString(error_code));
593  } else if (error_domain == chrome_common_net::kDnsProbeErrorDomain) {
594    std::string ascii_error_string =
595        chrome_common_net::DnsProbeStatusToString(error_code);
596    error_string = base::ASCIIToUTF16(ascii_error_string);
597  } else {
598    DCHECK_EQ(LocalizedError::kHttpErrorDomain, error_domain);
599    error_string = base::IntToString16(error_code);
600  }
601  error_strings->SetString("errorCode",
602      l10n_util::GetStringFUTF16(IDS_ERRORPAGES_ERROR_CODE, error_string));
603
604  // Platform specific information for diagnosing network issues on OSX and
605  // Windows.
606#if defined(OS_MACOSX) || defined(OS_WIN)
607  if (error_domain == net::kErrorDomain &&
608      error_code == net::ERR_INTERNET_DISCONNECTED) {
609    int platform_string_id =
610        IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM;
611#if defined(OS_WIN)
612    // Different versions of Windows have different instructions.
613    base::win::Version windows_version = base::win::GetVersion();
614    if (windows_version < base::win::VERSION_VISTA) {
615      // XP, XP64, and Server 2003.
616      platform_string_id =
617          IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM_XP;
618    } else if (windows_version == base::win::VERSION_VISTA) {
619      // Vista
620      platform_string_id =
621          IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM_VISTA;
622    }
623#endif  // defined(OS_WIN)
624    // Platform dependent portion of the summary section.
625    summary->SetString("msg",
626        l10n_util::GetStringFUTF16(
627            IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_INSTRUCTIONS_TEMPLATE,
628            l10n_util::GetStringUTF16(platform_string_id)));
629  }
630#endif  // defined(OS_MACOSX) || defined(OS_WIN)
631
632  // If no parameters were provided, use the defaults.
633  if (!params) {
634    params.reset(new ErrorPageParams());
635    params->suggest_reload = !!(options.suggestions & SUGGEST_RELOAD);
636  }
637
638  base::ListValue* suggestions = NULL;
639  bool use_default_suggestions = true;
640  if (!params->override_suggestions) {
641    suggestions = new base::ListValue();
642  } else {
643    suggestions = params->override_suggestions.release();
644    use_default_suggestions = false;
645  }
646
647  error_strings->Set("suggestions", suggestions);
648
649  if (params->search_url.is_valid()) {
650    error_strings->SetString("searchHeader",
651        l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_GOOGLE_SEARCH));
652    error_strings->SetString("searchUrl", params->search_url.spec());
653    error_strings->SetString("searchTerms", params->search_terms);
654    error_strings->SetInteger("searchTrackingId", params->search_tracking_id);
655  }
656
657  // Add the reload suggestion, if needed.
658  if (params->suggest_reload) {
659    if (!is_post) {
660      base::DictionaryValue* reload_button = new base::DictionaryValue;
661      reload_button->SetString(
662          "msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD));
663      reload_button->SetString("reloadUrl", failed_url.spec());
664      error_strings->Set("reloadButton", reload_button);
665      reload_button->SetInteger("reloadTrackingId", params->reload_tracking_id);
666    } else {
667      // If the page was created by a post, it can't be reloaded in the same
668      // way, so just add a suggestion instead.
669      // TODO(mmenke):  Make the reload button bring up the repost confirmation
670      //                dialog for pages resulting from posts.
671      base::DictionaryValue* suggest_reload_repost = new base::DictionaryValue;
672      suggest_reload_repost->SetString("header",
673          l10n_util::GetStringUTF16(
674              IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_HEADER));
675      suggest_reload_repost->SetString("body",
676          l10n_util::GetStringUTF16(
677              IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_BODY));
678      // Add at the front, so it appears before other suggestions, in the case
679      // suggestions are being overridden by |params|.
680      suggestions->Insert(0, suggest_reload_repost);
681    }
682  }
683
684  // If not using the default suggestions, nothing else to do.
685  if (!use_default_suggestions)
686    return;
687
688  if (show_stale_load_button) {
689    base::DictionaryValue* stale_load_button = new base::DictionaryValue;
690    stale_load_button->SetString(
691        "msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LOAD_STALE));
692    stale_load_button->SetString(
693        "title",
694        l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LOAD_STALE_HELP));
695    error_strings->Set("staleLoadButton", stale_load_button);
696  }
697
698#if defined(OS_CHROMEOS)
699  error_strings->SetString(
700      "diagnose", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_DIAGNOSE));
701#endif  // defined(OS_CHROMEOS)
702
703  if (options.suggestions & SUGGEST_CHECK_CONNECTION) {
704    base::DictionaryValue* suggest_check_connection = new base::DictionaryValue;
705    suggest_check_connection->SetString("header",
706        l10n_util::GetStringUTF16(
707            IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER));
708    suggest_check_connection->SetString("body",
709        l10n_util::GetStringUTF16(
710            IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_BODY));
711    suggestions->Append(suggest_check_connection);
712  }
713
714  if (options.suggestions & SUGGEST_DNS_CONFIG) {
715    base::DictionaryValue* suggest_dns_config = new base::DictionaryValue;
716    suggest_dns_config->SetString("header",
717        l10n_util::GetStringUTF16(
718            IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG_HEADER));
719    suggest_dns_config->SetString("body",
720        l10n_util::GetStringUTF16(
721            IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG_BODY));
722    suggestions->Append(suggest_dns_config);
723
724    base::DictionaryValue* suggest_network_prediction =
725        GetStandardMenuItemsText();
726    suggest_network_prediction->SetString("header",
727        l10n_util::GetStringUTF16(
728            IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION_HEADER));
729    suggest_network_prediction->SetString("body",
730        l10n_util::GetStringUTF16(
731            IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION_BODY));
732    suggest_network_prediction->SetString(
733        "noNetworkPredictionTitle",
734        l10n_util::GetStringUTF16(
735            IDS_NETWORK_PREDICTION_ENABLED_DESCRIPTION));
736    suggestions->Append(suggest_network_prediction);
737  }
738
739  if (options.suggestions & SUGGEST_FIREWALL_CONFIG) {
740    base::DictionaryValue* suggest_firewall_config = new base::DictionaryValue;
741    suggest_firewall_config->SetString("header",
742        l10n_util::GetStringUTF16(
743            IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG_HEADER));
744    suggest_firewall_config->SetString("body",
745        l10n_util::GetStringUTF16(
746            IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG_BODY));
747    suggestions->Append(suggest_firewall_config);
748  }
749
750  if (options.suggestions & SUGGEST_PROXY_CONFIG) {
751    base::DictionaryValue* suggest_proxy_config = GetStandardMenuItemsText();
752    suggest_proxy_config->SetString("header",
753        l10n_util::GetStringUTF16(
754            IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG_HEADER));
755    suggest_proxy_config->SetString("body",
756        l10n_util::GetStringFUTF16(IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG_BODY,
757            l10n_util::GetStringUTF16(
758                IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM)));
759    suggest_proxy_config->SetString("proxyTitle",
760        l10n_util::GetStringUTF16(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON));
761
762    suggestions->Append(suggest_proxy_config);
763  }
764
765  if (options.suggestions & SUGGEST_DISABLE_EXTENSION) {
766    base::DictionaryValue* suggest_disable_extension =
767        new base::DictionaryValue;
768    // There's only a header for this suggestion.
769    suggest_disable_extension->SetString("header",
770        l10n_util::GetStringUTF16(
771            IDS_ERRORPAGES_SUGGESTION_DISABLE_EXTENSION_HEADER));
772    suggestions->Append(suggest_disable_extension);
773  }
774
775  if (options.suggestions & SUGGEST_VIEW_POLICIES) {
776    base::DictionaryValue* suggest_view_policies = new base::DictionaryValue;
777    suggest_view_policies->SetString(
778        "header",
779        l10n_util::GetStringUTF16(
780            IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES_HEADER));
781    suggest_view_policies->SetString(
782        "body",
783        l10n_util::GetStringUTF16(
784            IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES_BODY));
785    suggestions->Append(suggest_view_policies);
786  }
787
788  if (options.suggestions & SUGGEST_CONTACT_ADMINISTRATOR) {
789    base::DictionaryValue* suggest_contant_administrator =
790        new base::DictionaryValue;
791    suggest_contant_administrator->SetString(
792        "body",
793        l10n_util::GetStringUTF16(
794            IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMINISTRATOR_BODY));
795    suggestions->Append(suggest_contant_administrator);
796  }
797
798  if (options.suggestions & SUGGEST_LEARNMORE) {
799    GURL learn_more_url;
800    switch (options.error_code) {
801      case net::ERR_TOO_MANY_REDIRECTS:
802        learn_more_url = GURL(kRedirectLoopLearnMoreUrl);
803        break;
804      case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY:
805        learn_more_url = GURL(kWeakDHKeyLearnMoreUrl);
806        break;
807      default:
808        break;
809    }
810
811    if (learn_more_url.is_valid()) {
812      // Add the language parameter to the URL.
813      std::string query = learn_more_url.query() + "&hl=" + locale;
814      GURL::Replacements repl;
815      repl.SetQueryStr(query);
816      learn_more_url = learn_more_url.ReplaceComponents(repl);
817
818      base::DictionaryValue* suggest_learn_more = new base::DictionaryValue;
819      // There's only a body for this suggestion.
820      suggest_learn_more->SetString("body",
821          l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_LEARNMORE_BODY));
822      suggest_learn_more->SetString("learnMoreUrl", learn_more_url.spec());
823      suggestions->Append(suggest_learn_more);
824    }
825  }
826}
827
828base::string16 LocalizedError::GetErrorDetails(const blink::WebURLError& error,
829                                               bool is_post) {
830  const LocalizedErrorMap* error_map =
831      LookupErrorMap(error.domain.utf8(), error.reason, is_post);
832  if (error_map)
833    return l10n_util::GetStringUTF16(error_map->details_resource_id);
834  else
835    return l10n_util::GetStringUTF16(IDS_ERRORPAGES_DETAILS_UNKNOWN);
836}
837
838bool LocalizedError::HasStrings(const std::string& error_domain,
839                                int error_code) {
840  // Whether or not the there are strings for an error does not depend on
841  // whether or not the page was be generated by a POST, so just claim it was
842  // not.
843  return LookupErrorMap(error_domain, error_code, /*is_post=*/false) != NULL;
844}
845
846void LocalizedError::GetAppErrorStrings(
847    const GURL& display_url,
848    const extensions::Extension* app,
849    base::DictionaryValue* error_strings) {
850  DCHECK(app);
851
852  bool rtl = LocaleIsRTL();
853  error_strings->SetString("textdirection", rtl ? "rtl" : "ltr");
854
855  base::string16 failed_url(base::ASCIIToUTF16(display_url.spec()));
856  // URLs are always LTR.
857  if (rtl)
858    base::i18n::WrapStringWithLTRFormatting(&failed_url);
859  error_strings->SetString(
860     "url", l10n_util::GetStringFUTF16(IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
861                                       failed_url.c_str()));
862
863  error_strings->SetString("title", app->name());
864  error_strings->SetString(
865      "icon",
866      extensions::IconsInfo::GetIconURL(
867          app,
868          extension_misc::EXTENSION_ICON_GIGANTOR,
869          ExtensionIconSet::MATCH_SMALLER).spec());
870  error_strings->SetString("name", app->name());
871  error_strings->SetString(
872      "msg",
873      l10n_util::GetStringUTF16(IDS_ERRORPAGES_APP_WARNING));
874
875#if defined(OS_CHROMEOS)
876  GURL learn_more_url(kAppWarningLearnMoreUrl);
877  base::DictionaryValue* suggest_learn_more = new base::DictionaryValue();
878  suggest_learn_more->SetString("msg",
879                                l10n_util::GetStringUTF16(
880                                    IDS_ERRORPAGES_SUGGESTION_LEARNMORE_BODY));
881  suggest_learn_more->SetString("learnMoreUrl", learn_more_url.spec());
882  error_strings->Set("suggestionsLearnMore", suggest_learn_more);
883#endif  // defined(OS_CHROMEOS)
884}
885