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 "extensions/common/constants.h" 16 #include "extensions/common/extension_icon_set.h" 17 #include "extensions/common/manifest_handlers/icons_handler.h" 18 #include "grit/chromium_strings.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/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 31 using blink::WebURLError; 32 33 // Some error pages have no details. 34 const unsigned int kErrorPagesNoDetails = 0; 35 36 namespace { 37 38 static const char kRedirectLoopLearnMoreUrl[] = 39 "https://www.google.com/support/chrome/bin/answer.py?answer=95626"; 40 static 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) 44 static const char kAppWarningLearnMoreUrl[] = 45 "chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html" 46 "?answer=1721911"; 47 #endif // defined(OS_CHROMEOS) 48 49 enum 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 62 struct 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 74 const 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. 309 const 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 318 const 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 378 const 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 425 const 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 435 const 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 462 bool 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. 468 base::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|. 478 const 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 491 const char LocalizedError::kHttpErrorDomain[] = "http"; 492 493 LocalizedError::ErrorPageParams::ErrorPageParams() 494 : suggest_reload(false), 495 reload_tracking_id(-1), 496 search_tracking_id(-1) { 497 } 498 499 LocalizedError::ErrorPageParams::~ErrorPageParams() { 500 } 501 502 void 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 514 // Grab the strings and settings that depend on the error type. Init 515 // options with default values. 516 LocalizedErrorMap options = { 517 0, 518 IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, 519 IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, 520 IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, 521 kErrorPagesNoDetails, 522 SUGGEST_NONE, 523 }; 524 525 const LocalizedErrorMap* error_map = LookupErrorMap(error_domain, error_code, 526 is_post); 527 if (error_map) 528 options = *error_map; 529 530 // If we got "access denied" but the url was a file URL, then we say it was a 531 // file instead of just using the "not available" default message. Just adding 532 // ERR_ACCESS_DENIED to the map isn't sufficient, since that message may be 533 // generated by some OSs when the operation doesn't involve a file URL. 534 if (error_domain == net::kErrorDomain && 535 error_code == net::ERR_ACCESS_DENIED && 536 failed_url.scheme() == "file") { 537 options.title_resource_id = IDS_ERRORPAGES_TITLE_ACCESS_DENIED; 538 options.heading_resource_id = IDS_ERRORPAGES_HEADING_FILE_ACCESS_DENIED; 539 options.summary_resource_id = IDS_ERRORPAGES_SUMMARY_FILE_ACCESS_DENIED; 540 options.details_resource_id = IDS_ERRORPAGES_DETAILS_FILE_ACCESS_DENIED; 541 options.suggestions = SUGGEST_NONE; 542 } 543 544 base::string16 failed_url_string(net::FormatUrl( 545 failed_url, accept_languages, net::kFormatUrlOmitNothing, 546 net::UnescapeRule::NORMAL, NULL, NULL, NULL)); 547 // URLs are always LTR. 548 if (rtl) 549 base::i18n::WrapStringWithLTRFormatting(&failed_url_string); 550 error_strings->SetString("title", 551 l10n_util::GetStringFUTF16(options.title_resource_id, failed_url_string)); 552 error_strings->SetString("heading", 553 l10n_util::GetStringUTF16(options.heading_resource_id)); 554 555 std::string icon_class = GetIconClassForError(error_domain, error_code); 556 error_strings->SetString("iconClass", icon_class); 557 558 base::DictionaryValue* summary = new base::DictionaryValue; 559 summary->SetString("msg", 560 l10n_util::GetStringUTF16(options.summary_resource_id)); 561 summary->SetString("failedUrl", failed_url_string); 562 summary->SetString("hostName", net::IDNToUnicode(failed_url.host(), 563 accept_languages)); 564 summary->SetString("productName", 565 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); 566 567 error_strings->SetString( 568 "more", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_MORE)); 569 error_strings->SetString( 570 "less", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LESS)); 571 error_strings->Set("summary", summary); 572 573 if (options.details_resource_id != kErrorPagesNoDetails) { 574 error_strings->SetString( 575 "errorDetails", l10n_util::GetStringUTF16(options.details_resource_id)); 576 } 577 578 base::string16 error_string; 579 if (error_domain == net::kErrorDomain) { 580 // Non-internationalized error string, for debugging Chrome itself. 581 std::string ascii_error_string = net::ErrorToString(error_code); 582 // Remove the leading "net::" from the returned string. 583 DCHECK(StartsWithASCII(ascii_error_string, "net::", true)); 584 ascii_error_string.erase(0, 5); 585 error_string = base::ASCIIToUTF16(ascii_error_string); 586 } else if (error_domain == chrome_common_net::kDnsProbeErrorDomain) { 587 std::string ascii_error_string = 588 chrome_common_net::DnsProbeStatusToString(error_code); 589 error_string = base::ASCIIToUTF16(ascii_error_string); 590 } else { 591 DCHECK_EQ(LocalizedError::kHttpErrorDomain, error_domain); 592 error_string = base::IntToString16(error_code); 593 } 594 error_strings->SetString("errorCode", 595 l10n_util::GetStringFUTF16(IDS_ERRORPAGES_ERROR_CODE, error_string)); 596 597 // Platform specific information for diagnosing network issues on OSX and 598 // Windows. 599 #if defined(OS_MACOSX) || defined(OS_WIN) 600 if (error_domain == net::kErrorDomain && 601 error_code == net::ERR_INTERNET_DISCONNECTED) { 602 int platform_string_id = 603 IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM; 604 #if defined(OS_WIN) 605 // Different versions of Windows have different instructions. 606 base::win::Version windows_version = base::win::GetVersion(); 607 if (windows_version < base::win::VERSION_VISTA) { 608 // XP, XP64, and Server 2003. 609 platform_string_id = 610 IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM_XP; 611 } else if (windows_version == base::win::VERSION_VISTA) { 612 // Vista 613 platform_string_id = 614 IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM_VISTA; 615 } 616 #endif // defined(OS_WIN) 617 // Lead with the general error description, and suffix with the platform 618 // dependent portion of the summary section. 619 summary->SetString("msg", 620 l10n_util::GetStringFUTF16( 621 IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_INSTRUCTIONS_TEMPLATE, 622 l10n_util::GetStringUTF16(options.summary_resource_id), 623 l10n_util::GetStringUTF16(platform_string_id))); 624 } 625 #endif // defined(OS_MACOSX) || defined(OS_WIN) 626 627 // If no parameters were provided, use the defaults. 628 if (!params) { 629 params.reset(new ErrorPageParams()); 630 params->suggest_reload = !!(options.suggestions & SUGGEST_RELOAD); 631 } 632 633 base::ListValue* suggestions = NULL; 634 bool use_default_suggestions = true; 635 if (!params->override_suggestions) { 636 suggestions = new base::ListValue(); 637 } else { 638 suggestions = params->override_suggestions.release(); 639 use_default_suggestions = false; 640 } 641 642 error_strings->Set("suggestions", suggestions); 643 644 if (params->search_url.is_valid()) { 645 error_strings->SetString("searchHeader", 646 l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_GOOGLE_SEARCH)); 647 error_strings->SetString("searchUrl", params->search_url.spec()); 648 error_strings->SetString("searchTerms", params->search_terms); 649 error_strings->SetInteger("searchTrackingId", params->search_tracking_id); 650 } 651 652 // Add the reload suggestion, if needed. 653 if (params->suggest_reload) { 654 if (!is_post) { 655 base::DictionaryValue* reload_button = new base::DictionaryValue; 656 reload_button->SetString( 657 "msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD)); 658 reload_button->SetString("reloadUrl", failed_url.spec()); 659 error_strings->Set("reloadButton", reload_button); 660 reload_button->SetInteger("reloadTrackingId", params->reload_tracking_id); 661 } else { 662 // If the page was created by a post, it can't be reloaded in the same 663 // way, so just add a suggestion instead. 664 // TODO(mmenke): Make the reload button bring up the repost confirmation 665 // dialog for pages resulting from posts. 666 base::DictionaryValue* suggest_reload_repost = new base::DictionaryValue; 667 suggest_reload_repost->SetString("header", 668 l10n_util::GetStringUTF16( 669 IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_HEADER)); 670 suggest_reload_repost->SetString("body", 671 l10n_util::GetStringUTF16( 672 IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_BODY)); 673 // Add at the front, so it appears before other suggestions, in the case 674 // suggestions are being overridden by |params|. 675 suggestions->Insert(0, suggest_reload_repost); 676 } 677 } 678 679 // If not using the default suggestions, nothing else to do. 680 if (!use_default_suggestions) 681 return; 682 683 if (show_stale_load_button) { 684 base::DictionaryValue* stale_load_button = new base::DictionaryValue; 685 stale_load_button->SetString( 686 "msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LOAD_STALE)); 687 stale_load_button->SetString( 688 "title", 689 l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LOAD_STALE_HELP)); 690 error_strings->Set("staleLoadButton", stale_load_button); 691 } 692 693 #if defined(OS_CHROMEOS) 694 error_strings->SetString( 695 "diagnose", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_DIAGNOSE)); 696 #endif // defined(OS_CHROMEOS) 697 698 if (options.suggestions & SUGGEST_CHECK_CONNECTION) { 699 base::DictionaryValue* suggest_check_connection = new base::DictionaryValue; 700 suggest_check_connection->SetString("header", 701 l10n_util::GetStringUTF16( 702 IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER)); 703 suggest_check_connection->SetString("body", 704 l10n_util::GetStringUTF16( 705 IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_BODY)); 706 suggestions->Append(suggest_check_connection); 707 } 708 709 if (options.suggestions & SUGGEST_DNS_CONFIG) { 710 base::DictionaryValue* suggest_dns_config = new base::DictionaryValue; 711 suggest_dns_config->SetString("header", 712 l10n_util::GetStringUTF16( 713 IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG_HEADER)); 714 suggest_dns_config->SetString("body", 715 l10n_util::GetStringUTF16( 716 IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG_BODY)); 717 suggestions->Append(suggest_dns_config); 718 719 base::DictionaryValue* suggest_network_prediction = 720 GetStandardMenuItemsText(); 721 suggest_network_prediction->SetString("header", 722 l10n_util::GetStringUTF16( 723 IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION_HEADER)); 724 suggest_network_prediction->SetString("body", 725 l10n_util::GetStringUTF16( 726 IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION_BODY)); 727 suggest_network_prediction->SetString( 728 "noNetworkPredictionTitle", 729 l10n_util::GetStringUTF16( 730 IDS_NETWORK_PREDICTION_ENABLED_DESCRIPTION)); 731 suggestions->Append(suggest_network_prediction); 732 } 733 734 if (options.suggestions & SUGGEST_FIREWALL_CONFIG) { 735 base::DictionaryValue* suggest_firewall_config = new base::DictionaryValue; 736 suggest_firewall_config->SetString("header", 737 l10n_util::GetStringUTF16( 738 IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG_HEADER)); 739 suggest_firewall_config->SetString("body", 740 l10n_util::GetStringUTF16( 741 IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG_BODY)); 742 suggestions->Append(suggest_firewall_config); 743 } 744 745 if (options.suggestions & SUGGEST_PROXY_CONFIG) { 746 base::DictionaryValue* suggest_proxy_config = GetStandardMenuItemsText(); 747 suggest_proxy_config->SetString("header", 748 l10n_util::GetStringUTF16( 749 IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG_HEADER)); 750 suggest_proxy_config->SetString("body", 751 l10n_util::GetStringFUTF16(IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG_BODY, 752 l10n_util::GetStringUTF16( 753 IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM))); 754 suggest_proxy_config->SetString("proxyTitle", 755 l10n_util::GetStringUTF16(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON)); 756 757 suggestions->Append(suggest_proxy_config); 758 } 759 760 if (options.suggestions & SUGGEST_DISABLE_EXTENSION) { 761 base::DictionaryValue* suggest_disable_extension = 762 new base::DictionaryValue; 763 // There's only a header for this suggestion. 764 suggest_disable_extension->SetString("header", 765 l10n_util::GetStringUTF16( 766 IDS_ERRORPAGES_SUGGESTION_DISABLE_EXTENSION_HEADER)); 767 suggestions->Append(suggest_disable_extension); 768 } 769 770 if (options.suggestions & SUGGEST_VIEW_POLICIES) { 771 base::DictionaryValue* suggest_view_policies = new base::DictionaryValue; 772 suggest_view_policies->SetString( 773 "header", 774 l10n_util::GetStringUTF16( 775 IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES_HEADER)); 776 suggest_view_policies->SetString( 777 "body", 778 l10n_util::GetStringUTF16( 779 IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES_BODY)); 780 suggestions->Append(suggest_view_policies); 781 } 782 783 if (options.suggestions & SUGGEST_CONTACT_ADMINISTRATOR) { 784 base::DictionaryValue* suggest_contant_administrator = 785 new base::DictionaryValue; 786 suggest_contant_administrator->SetString( 787 "body", 788 l10n_util::GetStringUTF16( 789 IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMINISTRATOR_BODY)); 790 suggestions->Append(suggest_contant_administrator); 791 } 792 793 if (options.suggestions & SUGGEST_LEARNMORE) { 794 GURL learn_more_url; 795 switch (options.error_code) { 796 case net::ERR_TOO_MANY_REDIRECTS: 797 learn_more_url = GURL(kRedirectLoopLearnMoreUrl); 798 break; 799 case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: 800 learn_more_url = GURL(kWeakDHKeyLearnMoreUrl); 801 break; 802 default: 803 break; 804 } 805 806 if (learn_more_url.is_valid()) { 807 // Add the language parameter to the URL. 808 std::string query = learn_more_url.query() + "&hl=" + locale; 809 GURL::Replacements repl; 810 repl.SetQueryStr(query); 811 learn_more_url = learn_more_url.ReplaceComponents(repl); 812 813 base::DictionaryValue* suggest_learn_more = new base::DictionaryValue; 814 // There's only a body for this suggestion. 815 suggest_learn_more->SetString("body", 816 l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_LEARNMORE_BODY)); 817 suggest_learn_more->SetString("learnMoreUrl", learn_more_url.spec()); 818 suggestions->Append(suggest_learn_more); 819 } 820 } 821 } 822 823 base::string16 LocalizedError::GetErrorDetails(const blink::WebURLError& error, 824 bool is_post) { 825 const LocalizedErrorMap* error_map = 826 LookupErrorMap(error.domain.utf8(), error.reason, is_post); 827 if (error_map) 828 return l10n_util::GetStringUTF16(error_map->details_resource_id); 829 else 830 return l10n_util::GetStringUTF16(IDS_ERRORPAGES_DETAILS_UNKNOWN); 831 } 832 833 bool LocalizedError::HasStrings(const std::string& error_domain, 834 int error_code) { 835 // Whether or not the there are strings for an error does not depend on 836 // whether or not the page was be generated by a POST, so just claim it was 837 // not. 838 return LookupErrorMap(error_domain, error_code, /*is_post=*/false) != NULL; 839 } 840 841 void LocalizedError::GetAppErrorStrings( 842 const GURL& display_url, 843 const extensions::Extension* app, 844 base::DictionaryValue* error_strings) { 845 DCHECK(app); 846 847 bool rtl = LocaleIsRTL(); 848 error_strings->SetString("textdirection", rtl ? "rtl" : "ltr"); 849 850 base::string16 failed_url(base::ASCIIToUTF16(display_url.spec())); 851 // URLs are always LTR. 852 if (rtl) 853 base::i18n::WrapStringWithLTRFormatting(&failed_url); 854 error_strings->SetString( 855 "url", l10n_util::GetStringFUTF16(IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, 856 failed_url.c_str())); 857 858 error_strings->SetString("title", app->name()); 859 error_strings->SetString( 860 "icon", 861 extensions::IconsInfo::GetIconURL( 862 app, 863 extension_misc::EXTENSION_ICON_GIGANTOR, 864 ExtensionIconSet::MATCH_SMALLER).spec()); 865 error_strings->SetString("name", app->name()); 866 error_strings->SetString( 867 "msg", 868 l10n_util::GetStringUTF16(IDS_ERRORPAGES_APP_WARNING)); 869 870 #if defined(OS_CHROMEOS) 871 GURL learn_more_url(kAppWarningLearnMoreUrl); 872 base::DictionaryValue* suggest_learn_more = new base::DictionaryValue(); 873 suggest_learn_more->SetString("msg", 874 l10n_util::GetStringUTF16( 875 IDS_ERRORPAGES_SUGGESTION_LEARNMORE_BODY)); 876 suggest_learn_more->SetString("learnMoreUrl", learn_more_url.spec()); 877 error_strings->Set("suggestionsLearnMore", suggest_learn_more); 878 #endif // defined(OS_CHROMEOS) 879 } 880