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