| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "components/error_page/common/localized_error.h" | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <memory> | 
 | #include <utility> | 
 |  | 
 | #include "base/check_op.h" | 
 | #include "base/command_line.h" | 
 | #include "base/i18n/rtl.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/metrics/field_trial.h" | 
 | #include "base/notreached.h" | 
 | #include "base/stl_util.h" | 
 | #include "base/strings/string16.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/values.h" | 
 | #include "build/build_config.h" | 
 | #include "build/chromeos_buildflags.h" | 
 | #include "components/error_page/common/error.h" | 
 | #include "components/error_page/common/error_page_switches.h" | 
 | #include "components/error_page/common/net_error_info.h" | 
 | #include "components/offline_pages/core/offline_page_feature.h" | 
 | #include "components/strings/grit/components_chromium_strings.h" | 
 | #include "components/strings/grit/components_strings.h" | 
 | #include "components/url_formatter/url_formatter.h" | 
 | #include "net/base/escape.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "ui/base/l10n/l10n_util.h" | 
 | #include "ui/base/webui/web_ui_util.h" | 
 | #include "url/origin.h" | 
 |  | 
 | #if defined(OS_WIN) | 
 | #include "base/win/windows_version.h" | 
 | #endif | 
 |  | 
 | namespace error_page { | 
 |  | 
 | namespace { | 
 |  | 
 | // Hardcode these constants to avoid dependences on //chrome and //content. | 
 | const char kChromeUIScheme[] = "chrome"; | 
 | const char kChromeUIDinoHost[] = "dino"; | 
 |  | 
 | static const char kRedirectLoopLearnMoreUrl[] = | 
 |     "https://support.google.com/chrome?p=rl_error"; | 
 |  | 
 | enum NAV_SUGGESTIONS { | 
 |   SUGGEST_NONE = 0, | 
 |   SUGGEST_DIAGNOSE_TOOL = 1 << 0, | 
 |   SUGGEST_CHECK_CONNECTION = 1 << 1, | 
 |   SUGGEST_DNS_CONFIG = 1 << 2, | 
 |   SUGGEST_FIREWALL_CONFIG = 1 << 3, | 
 |   SUGGEST_PROXY_CONFIG = 1 << 4, | 
 |   SUGGEST_DISABLE_EXTENSION = 1 << 5, | 
 |   SUGGEST_LEARNMORE = 1 << 6, | 
 |   SUGGEST_CONTACT_ADMINISTRATOR = 1 << 7, | 
 |   SUGGEST_UNSUPPORTED_CIPHER = 1 << 8, | 
 |   SUGGEST_ANTIVIRUS_CONFIG = 1 << 9, | 
 |   SUGGEST_OFFLINE_CHECKS = 1 << 10, | 
 |   SUGGEST_COMPLETE_SETUP = 1 << 11, | 
 |   // Reload page suggestion for pages created by a post. | 
 |   SUGGEST_REPOST_RELOAD = 1 << 12, | 
 |   SUGGEST_NAVIGATE_TO_ORIGIN = 1 << 13, | 
 |   SUGGEST_SECURE_DNS_CONFIG = 1 << 14, | 
 | }; | 
 |  | 
 | enum SHOW_BUTTONS { | 
 |   SHOW_NO_BUTTONS    = 0, | 
 |   SHOW_BUTTON_RELOAD = 1, | 
 | }; | 
 |  | 
 | struct LocalizedErrorMap { | 
 |   int error_code; | 
 |   unsigned int heading_resource_id; | 
 |   // Detailed summary used when the error is in the main frame and shown on | 
 |   // mouse over when the error is in a frame. | 
 |   unsigned int summary_resource_id; | 
 |   int suggestions;  // Bitmap of SUGGEST_* values. | 
 |   int buttons;      // Bitmap of which buttons if any to show. | 
 | }; | 
 |  | 
 | // clang-format off | 
 | const LocalizedErrorMap net_error_options[] = { | 
 |   {net::ERR_TIMED_OUT, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_TIMED_OUT, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG | | 
 |        SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CONNECTION_TIMED_OUT, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_TIMED_OUT, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG | | 
 |        SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CONNECTION_CLOSED, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_CLOSED, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG | | 
 |        SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CONNECTION_RESET, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_RESET, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG | | 
 |        SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CONNECTION_REFUSED, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_REFUSED, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CONNECTION_FAILED, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_FAILED, | 
 |    SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_NAME_NOT_RESOLVED, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_DNS_CONFIG | SUGGEST_FIREWALL_CONFIG | | 
 |        SUGGEST_PROXY_CONFIG | SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_ICANN_NAME_COLLISION, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_ICANN_NAME_COLLISION, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_ADDRESS_UNREACHABLE, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_ADDRESS_UNREACHABLE, | 
 |    SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_NETWORK_ACCESS_DENIED, | 
 |    IDS_ERRORPAGES_HEADING_NETWORK_ACCESS_DENIED, | 
 |    IDS_ERRORPAGES_SUMMARY_NETWORK_ACCESS_DENIED, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | | 
 |        SUGGEST_ANTIVIRUS_CONFIG | SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_PROXY_CONNECTION_FAILED, | 
 |    IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED, | 
 |    IDS_ERRORPAGES_SUMMARY_PROXY_CONNECTION_FAILED, | 
 |    SUGGEST_PROXY_CONFIG | SUGGEST_CONTACT_ADMINISTRATOR | SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_INTERNET_DISCONNECTED, | 
 |    IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED, | 
 |    IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED, | 
 |    SUGGEST_OFFLINE_CHECKS | SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_FILE_NOT_FOUND, | 
 |    IDS_ERRORPAGES_HEADING_FILE_NOT_FOUND, | 
 |    IDS_ERRORPAGES_SUMMARY_FILE_NOT_FOUND, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_UPLOAD_FILE_CHANGED, | 
 |    IDS_ERRORPAGES_HEADING_FILE_NOT_FOUND, | 
 |    IDS_ERRORPAGES_SUMMARY_FILE_NOT_FOUND, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_CACHE_MISS, | 
 |    IDS_ERRORPAGES_HEADING_CACHE_READ_FAILURE, | 
 |    IDS_ERRORPAGES_SUMMARY_CACHE_READ_FAILURE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CACHE_READ_FAILURE, | 
 |    IDS_ERRORPAGES_HEADING_CACHE_READ_FAILURE, | 
 |    IDS_ERRORPAGES_SUMMARY_CACHE_READ_FAILURE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_NETWORK_IO_SUSPENDED, | 
 |    IDS_ERRORPAGES_HEADING_CONNECTION_INTERRUPTED, | 
 |    IDS_ERRORPAGES_SUMMARY_NETWORK_IO_SUSPENDED, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_TOO_MANY_REDIRECTS, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_TOO_MANY_REDIRECTS, | 
 |    SUGGEST_LEARNMORE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_EMPTY_RESPONSE, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_EMPTY_RESPONSE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_INVALID_HTTP_RESPONSE, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_CONTENT_LENGTH_MISMATCH, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_CLOSED, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_INCOMPLETE_CHUNKED_ENCODING, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_CLOSED, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_INVALID_REDIRECT, | 
 |    IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |    IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_SSL_PROTOCOL_ERROR, | 
 |    IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION, | 
 |    IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE, | 
 |    SUGGEST_DIAGNOSE_TOOL, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_BAD_SSL_CLIENT_AUTH_CERT, | 
 |    IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION, | 
 |    IDS_ERRORPAGES_SUMMARY_BAD_SSL_CLIENT_AUTH_CERT, | 
 |    SUGGEST_CONTACT_ADMINISTRATOR, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_TLS13_DOWNGRADE_DETECTED, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_FAILED, | 
 |    SUGGEST_CHECK_CONNECTION | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, | 
 |    IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION, | 
 |    IDS_CERT_ERROR_SUMMARY_PINNING_FAILURE_DETAILS, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_TEMPORARILY_THROTTLED, | 
 |    IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |    IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, | 
 |    SUGGEST_DISABLE_EXTENSION, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_BLOCKED_BY_CLIENT, | 
 |    IDS_ERRORPAGES_HEADING_BLOCKED, | 
 |    IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_EXTENSION, | 
 |    SUGGEST_DISABLE_EXTENSION, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_BLOCKED_BY_CSP, | 
 |    IDS_ERRORPAGES_HEADING_BLOCKED, | 
 |    IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_SECURITY, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_NETWORK_CHANGED, | 
 |    IDS_ERRORPAGES_HEADING_CONNECTION_INTERRUPTED, | 
 |    IDS_ERRORPAGES_SUMMARY_NETWORK_CHANGED, | 
 |    SUGGEST_NONE, | 
 |    SHOW_BUTTON_RELOAD, | 
 |   }, | 
 |   {net::ERR_BLOCKED_BY_ADMINISTRATOR, | 
 |    IDS_ERRORPAGES_HEADING_BLOCKED, | 
 |    IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_ADMINISTRATOR, | 
 |    SUGGEST_CONTACT_ADMINISTRATOR, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_BLOCKED_ENROLLMENT_CHECK_PENDING, | 
 |    IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED, | 
 |    IDS_ERRORPAGES_SUMMARY_BLOCKED_ENROLLMENT_CHECK_PENDING, | 
 |    SUGGEST_COMPLETE_SETUP, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH, | 
 |    IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION, | 
 |    IDS_ERRORPAGES_SUMMARY_SSL_VERSION_OR_CIPHER_MISMATCH, | 
 |    SUGGEST_UNSUPPORTED_CIPHER, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_SSL_OBSOLETE_CIPHER, | 
 |    IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION, | 
 |    IDS_ERRORPAGES_SUMMARY_SSL_VERSION_OR_CIPHER_MISMATCH, | 
 |    SUGGEST_UNSUPPORTED_CIPHER, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_SSL_SERVER_CERT_BAD_FORMAT, | 
 |    IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION, | 
 |    IDS_ERRORPAGES_SUMMARY_SSL_SECURITY_ERROR, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS, | 
 |   }, | 
 |   {net::ERR_BLOCKED_BY_RESPONSE, | 
 |    IDS_ERRORPAGES_HEADING_BLOCKED, | 
 |    IDS_ERRORPAGES_SUMMARY_CONNECTION_REFUSED, | 
 |    SUGGEST_NONE, | 
 |    SHOW_NO_BUTTONS | 
 |   }, | 
 | }; | 
 | // clang-format on | 
 |  | 
 | // Special error page to be used in the case of navigating back to a page | 
 | // generated by a POST.  LocalizedError::HasStrings expects this net error code | 
 | // to also appear in the array above. | 
 | const LocalizedErrorMap repost_error = { | 
 |   net::ERR_CACHE_MISS, | 
 |   IDS_HTTP_POST_WARNING_TITLE, | 
 |   IDS_ERRORPAGES_HTTP_POST_WARNING, | 
 |   SUGGEST_REPOST_RELOAD, | 
 |   SHOW_NO_BUTTONS, | 
 | }; | 
 |  | 
 | // Special error page to be used for hostname resolution errors that resulted | 
 | // from secure DNS network failures.  LocalizedError::HasStrings expects this | 
 | // net error code to also appear in the array above. | 
 | const LocalizedErrorMap secure_dns_network_error = { | 
 |     net::ERR_NAME_NOT_RESOLVED, | 
 |     IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |     IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED, | 
 |     SUGGEST_CHECK_CONNECTION | SUGGEST_SECURE_DNS_CONFIG | | 
 |         SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG | SUGGEST_DIAGNOSE_TOOL, | 
 |     SHOW_BUTTON_RELOAD, | 
 | }; | 
 |  | 
 | const LocalizedErrorMap http_error_options[] = { | 
 |     { | 
 |         403, IDS_ERRORPAGES_HEADING_ACCESS_DENIED, | 
 |         IDS_ERRORPAGES_SUMMARY_FORBIDDEN, SUGGEST_NONE, SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         404, IDS_ERRORPAGES_HEADING_NOT_FOUND, IDS_ERRORPAGES_SUMMARY_NOT_FOUND, | 
 |         SUGGEST_NONE, SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         410, IDS_ERRORPAGES_HEADING_NOT_FOUND, IDS_ERRORPAGES_SUMMARY_GONE, | 
 |         SUGGEST_NONE, SHOW_NO_BUTTONS, | 
 |     }, | 
 |  | 
 |     { | 
 |         500, IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |         IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE_REQUEST, SUGGEST_NONE, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         501, IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |         IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE_REQUEST, SUGGEST_NONE, | 
 |         SHOW_NO_BUTTONS, | 
 |     }, | 
 |     { | 
 |         502, IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |         IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE_REQUEST, SUGGEST_NONE, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         503, IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |         IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE_REQUEST, SUGGEST_NONE, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         504, IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |         IDS_ERRORPAGES_SUMMARY_GATEWAY_TIMEOUT, SUGGEST_NONE, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 | }; | 
 |  | 
 | const LocalizedErrorMap generic_4xx_5xx_error = { | 
 |     0, | 
 |     IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING, | 
 |     IDS_ERRORPAGES_SUMMARY_CONTACT_SITE_OWNER, | 
 |     SUGGEST_NONE, | 
 |     SHOW_BUTTON_RELOAD, | 
 | }; | 
 |  | 
 | const LocalizedErrorMap dns_probe_error_options[] = { | 
 |     { | 
 |         error_page::DNS_PROBE_POSSIBLE, | 
 |         IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |         IDS_ERRORPAGES_SUMMARY_DNS_PROBE_RUNNING, | 
 |         SUGGEST_DIAGNOSE_TOOL, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |  | 
 |     // DNS_PROBE_NOT_RUN is not here; NetErrorHelper will restore the original | 
 |     // error, which might be one of several DNS-related errors. | 
 |  | 
 |     { | 
 |         error_page::DNS_PROBE_STARTED, | 
 |         IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |         IDS_ERRORPAGES_SUMMARY_DNS_PROBE_RUNNING, | 
 |         // Include SUGGEST_RELOAD so the More button doesn't jump when we | 
 |         // update. | 
 |         SUGGEST_DIAGNOSE_TOOL, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |  | 
 |     // DNS_PROBE_FINISHED_UNKNOWN is not here; NetErrorHelper will restore the | 
 |     // original error, which might be one of several DNS-related errors. | 
 |  | 
 |     { | 
 |         error_page::DNS_PROBE_FINISHED_NO_INTERNET, | 
 |         IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED, | 
 |         IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED, | 
 |         SUGGEST_OFFLINE_CHECKS | SUGGEST_DIAGNOSE_TOOL, | 
 |         SHOW_NO_BUTTONS, | 
 |     }, | 
 |     { | 
 |         error_page::DNS_PROBE_FINISHED_BAD_CONFIG, | 
 |         IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |         IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED, | 
 |         SUGGEST_DNS_CONFIG | SUGGEST_FIREWALL_CONFIG | SUGGEST_PROXY_CONFIG | | 
 |             SUGGEST_DIAGNOSE_TOOL, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         error_page::DNS_PROBE_FINISHED_BAD_SECURE_CONFIG, | 
 |         IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |         IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED, | 
 |         SUGGEST_SECURE_DNS_CONFIG | SUGGEST_FIREWALL_CONFIG | | 
 |             SUGGEST_PROXY_CONFIG | SUGGEST_DIAGNOSE_TOOL, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 |     { | 
 |         error_page::DNS_PROBE_FINISHED_NXDOMAIN, | 
 |         IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |         IDS_ERRORPAGES_CHECK_TYPO_SUMMARY, | 
 |         SUGGEST_DIAGNOSE_TOOL, | 
 |         SHOW_BUTTON_RELOAD, | 
 |     }, | 
 | }; | 
 |  | 
 | const LocalizedErrorMap* FindErrorMapInArray(const LocalizedErrorMap* maps, | 
 |                                                    size_t num_maps, | 
 |                                                    int error_code) { | 
 |   for (size_t i = 0; i < num_maps; ++i) { | 
 |     if (maps[i].error_code == error_code) | 
 |       return &maps[i]; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | const LocalizedErrorMap* LookupErrorMap(const std::string& error_domain, | 
 |                                         int error_code, | 
 |                                         bool is_secure_dns_network_error, | 
 |                                         bool is_post) { | 
 |   if (error_domain == Error::kNetErrorDomain) { | 
 |     // Display a different page in the special case of navigating through the | 
 |     // history to an uncached page created by a POST. | 
 |     if (is_post && error_code == net::ERR_CACHE_MISS) | 
 |       return &repost_error; | 
 |     // Display a different page in the special case of achieving a hostname | 
 |     // resolution error that was the result of a secure DNS network failure. | 
 |     if (is_secure_dns_network_error && | 
 |         net::IsHostnameResolutionError(error_code)) { | 
 |       return &secure_dns_network_error; | 
 |     } | 
 |     return FindErrorMapInArray(net_error_options, base::size(net_error_options), | 
 |                                error_code); | 
 |   } else if (error_domain == Error::kHttpErrorDomain) { | 
 |     const LocalizedErrorMap* map = FindErrorMapInArray( | 
 |         http_error_options, base::size(http_error_options), error_code); | 
 |     // Handle miscellaneous 400/500 errors. | 
 |     return !map && error_code >= 400 && error_code < 600 | 
 |                ? &generic_4xx_5xx_error | 
 |                : map; | 
 |   } else if (error_domain == Error::kDnsProbeErrorDomain) { | 
 |     const LocalizedErrorMap* map = | 
 |         FindErrorMapInArray(dns_probe_error_options, | 
 |                             base::size(dns_probe_error_options), error_code); | 
 |     DCHECK(map); | 
 |     return map; | 
 |   } else { | 
 |     NOTREACHED(); | 
 |     return nullptr; | 
 |   } | 
 | } | 
 |  | 
 | // Returns a dictionary containing the strings for the settings menu under the | 
 | // app menu, and the advanced settings button. | 
 | base::DictionaryValue* GetStandardMenuItemsText() { | 
 |   base::DictionaryValue* standard_menu_items_text = new base::DictionaryValue(); | 
 |   standard_menu_items_text->SetString("settingsTitle", | 
 |       l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE)); | 
 |   standard_menu_items_text->SetString("advancedTitle", | 
 |       l10n_util::GetStringUTF16(IDS_SETTINGS_SHOW_ADVANCED_SETTINGS)); | 
 |   return standard_menu_items_text; | 
 | } | 
 |  | 
 | // Returns true if the error is due to a disconnected network. | 
 | bool IsOfflineError(const std::string& error_domain, int error_code) { | 
 |   return ((error_code == net::ERR_INTERNET_DISCONNECTED && | 
 |            error_domain == Error::kNetErrorDomain) || | 
 |           (error_code == error_page::DNS_PROBE_FINISHED_NO_INTERNET && | 
 |            error_domain == Error::kDnsProbeErrorDomain)); | 
 | } | 
 |  | 
 | // Gets the icon class for a given |error_domain| and |error_code|. | 
 | const char* GetIconClassForError(const std::string& error_domain, | 
 |                                  int error_code) { | 
 |   return IsOfflineError(error_domain, error_code) ? "icon-offline" | 
 |                                                   : "icon-generic"; | 
 | } | 
 |  | 
 | // Helper function that creates a single entry dictionary and adds it | 
 | // to a ListValue, | 
 | void AddSingleEntryDictionaryToList(base::ListValue* list, | 
 |                                     const char* path, | 
 |                                     int message_id, | 
 |                                     bool insert_as_first_item) { | 
 |   auto suggestion_list_item = std::make_unique<base::DictionaryValue>(); | 
 |   suggestion_list_item->SetString(path, l10n_util::GetStringUTF16(message_id)); | 
 |  | 
 |   if (insert_as_first_item) { | 
 |     list->Insert(0, std::move(suggestion_list_item)); | 
 |   } else { | 
 |     list->Append(std::move(suggestion_list_item)); | 
 |   } | 
 | } | 
 |  | 
 | // Adds a linked suggestion dictionary entry to the suggestions list. | 
 | void AddLinkedSuggestionToList(const int error_code, | 
 |                                const std::string& locale, | 
 |                                base::ListValue* suggestions_summary_list, | 
 |                                bool standalone_suggestion) { | 
 |   GURL learn_more_url; | 
 |   base::string16 suggestion_string = standalone_suggestion ? | 
 |       l10n_util::GetStringUTF16( | 
 |           IDS_ERRORPAGES_SUGGESTION_LEARNMORE_SUMMARY_STANDALONE) : | 
 |       l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_LEARNMORE_SUMMARY); | 
 |  | 
 |   switch (error_code) { | 
 |     case net::ERR_TOO_MANY_REDIRECTS: | 
 |       learn_more_url = GURL(kRedirectLoopLearnMoreUrl); | 
 |       suggestion_string = l10n_util::GetStringUTF16( | 
 |           IDS_ERRORPAGES_SUGGESTION_CLEAR_COOKIES_SUMMARY); | 
 |       break; | 
 |     default: | 
 |       NOTREACHED(); | 
 |       break; | 
 |   } | 
 |  | 
 |   DCHECK(learn_more_url.is_valid()); | 
 |   // Add the language parameter to the URL. | 
 |   std::string query = learn_more_url.query() + "&hl=" + locale; | 
 |   GURL::Replacements repl; | 
 |   repl.SetQueryStr(query); | 
 |   GURL learn_more_url_with_locale = learn_more_url.ReplaceComponents(repl); | 
 |  | 
 |   std::unique_ptr<base::DictionaryValue> suggestion_list_item( | 
 |       new base::DictionaryValue); | 
 |   suggestion_list_item->SetString("summary", suggestion_string); | 
 |   suggestion_list_item->SetString("learnMoreUrl", | 
 |       learn_more_url_with_locale.spec()); | 
 |   suggestions_summary_list->Append(std::move(suggestion_list_item)); | 
 | } | 
 |  | 
 | // Check if a suggestion is in the bitmap of suggestions. | 
 | bool IsSuggested(int suggestions, int suggestion) { | 
 |   return !!(suggestions & suggestion); | 
 | } | 
 |  | 
 | // Check suggestion is the only item in the suggestions bitmap. | 
 | bool IsOnlySuggestion(int suggestions, int suggestion) { | 
 |   return IsSuggested(suggestions, suggestion) && !(suggestions & ~suggestion); | 
 | } | 
 |  | 
 | // Creates a list of suggestions that a user may try to resolve a particular | 
 | // network error. Appears above the fold underneath heading and intro paragraph. | 
 | void GetSuggestionsSummaryList(int error_code, | 
 |                                base::DictionaryValue* error_strings, | 
 |                                int suggestions, | 
 |                                const std::string& locale, | 
 |                                base::ListValue* suggestions_summary_list, | 
 |                                bool can_show_network_diagnostics_dialog, | 
 |                                const GURL& failed_url) { | 
 |   // Remove the diagnostic tool suggestion if the platform doesn't support it | 
 |   // or the url isn't valid. | 
 |   if (!can_show_network_diagnostics_dialog || !failed_url.is_valid() || | 
 |       !failed_url.SchemeIsHTTPOrHTTPS()) { | 
 |     suggestions &= ~SUGGEST_DIAGNOSE_TOOL; | 
 |   } | 
 |  | 
 |   if (suggestions == SUGGEST_NONE) | 
 |     return; | 
 |  | 
 |   if (IsOnlySuggestion(suggestions, SUGGEST_CONTACT_ADMINISTRATOR)) { | 
 |     DCHECK(suggestions_summary_list->empty()); | 
 |     DCHECK(!(suggestions & ~SUGGEST_CONTACT_ADMINISTRATOR)); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY_STANDALONE, false); | 
 |     return; | 
 |   } | 
 |   if (IsSuggested(suggestions, SUGGEST_CONTACT_ADMINISTRATOR)) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY, false); | 
 |   } | 
 |  | 
 |   if (IsOnlySuggestion(suggestions, SUGGEST_COMPLETE_SETUP)) { | 
 |     DCHECK(suggestions_summary_list->empty()); | 
 |     DCHECK(!(suggestions & ~SUGGEST_COMPLETE_SETUP)); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_DIAGNOSE_CONNECTION_SUMMARY, false); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_COMPLETE_SETUP_SUMMARY, false); | 
 |     return; | 
 |   } | 
 |   DCHECK(!IsSuggested(suggestions, SUGGEST_COMPLETE_SETUP)); | 
 |  | 
 |   if (IsOnlySuggestion(suggestions,SUGGEST_REPOST_RELOAD)) { | 
 |     DCHECK(suggestions_summary_list->empty()); | 
 |     DCHECK(!(suggestions & ~SUGGEST_REPOST_RELOAD)); | 
 |     // If the page was created by a post, it can't be reloaded in the same | 
 |     // way, so just add a suggestion instead. | 
 |     // TODO(mmenke):  Make the reload button bring up the repost confirmation | 
 |     //                dialog for pages resulting from posts. | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_SUMMARY, false); | 
 |     return; | 
 |   } | 
 |   DCHECK(!IsSuggested(suggestions, SUGGEST_REPOST_RELOAD)); | 
 |  | 
 |   if (IsOnlySuggestion(suggestions, SUGGEST_NAVIGATE_TO_ORIGIN)) { | 
 |     DCHECK(suggestions_summary_list->empty()); | 
 |     DCHECK(!(suggestions & ~SUGGEST_NAVIGATE_TO_ORIGIN)); | 
 |     url::Origin failed_origin = url::Origin::Create(failed_url); | 
 |     if (failed_origin.opaque()) | 
 |       return; | 
 |  | 
 |     auto suggestion = std::make_unique<base::DictionaryValue>(); | 
 |     suggestion->SetString("summary", | 
 |                           l10n_util::GetStringUTF16( | 
 |                               IDS_ERRORPAGES_SUGGESTION_NAVIGATE_TO_ORIGIN)); | 
 |     suggestion->SetString("originURL", failed_origin.Serialize()); | 
 |     suggestions_summary_list->Append(std::move(suggestion)); | 
 |     return; | 
 |   } | 
 |   DCHECK(!IsSuggested(suggestions, SUGGEST_NAVIGATE_TO_ORIGIN)); | 
 |  | 
 |   if (IsOnlySuggestion(suggestions, SUGGEST_LEARNMORE)) { | 
 |     DCHECK(suggestions_summary_list->empty()); | 
 |     AddLinkedSuggestionToList(error_code, locale, suggestions_summary_list, | 
 |                               true); | 
 |     return; | 
 |   } | 
 |   if (IsSuggested(suggestions, SUGGEST_LEARNMORE)) { | 
 |     AddLinkedSuggestionToList(error_code, locale, suggestions_summary_list, | 
 |                               false); | 
 |   } | 
 |  | 
 |   if (suggestions & SUGGEST_DISABLE_EXTENSION) { | 
 |     DCHECK(suggestions_summary_list->empty()); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_DISABLE_EXTENSION_SUMMARY, false); | 
 |     return; | 
 |   } | 
 |   DCHECK(!IsSuggested(suggestions, SUGGEST_DISABLE_EXTENSION)); | 
 |  | 
 |   if (suggestions & SUGGEST_CHECK_CONNECTION) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_SUMMARY, false); | 
 |   } | 
 |  | 
 | #if !defined(OS_ANDROID) && !defined(OS_IOS) | 
 |   if (IsSuggested(suggestions, SUGGEST_DNS_CONFIG) && | 
 |       IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG) && | 
 |       IsSuggested(suggestions, SUGGEST_PROXY_CONFIG)) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_FIREWALL_DNS_SUMMARY, false); | 
 |   } else if (IsSuggested(suggestions, SUGGEST_SECURE_DNS_CONFIG) && | 
 |              IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG) && | 
 |              IsSuggested(suggestions, SUGGEST_PROXY_CONFIG)) { | 
 |     AddSingleEntryDictionaryToList( | 
 |         suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_FIREWALL_SECURE_DNS_SUMMARY, | 
 |         false); | 
 |   } else if (IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG) && | 
 |              IsSuggested(suggestions, SUGGEST_ANTIVIRUS_CONFIG)) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_FIREWALL_ANTIVIRUS_SUMMARY, false); | 
 |   } else if (IsSuggested(suggestions, SUGGEST_PROXY_CONFIG) && | 
 |              IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG)) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_FIREWALL_SUMMARY, false); | 
 |   } else if (IsSuggested(suggestions, SUGGEST_PROXY_CONFIG)) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_ADDRESS_SUMMARY, false); | 
 |   } else { | 
 |     DCHECK(!(suggestions & SUGGEST_PROXY_CONFIG)); | 
 |     DCHECK(!(suggestions & SUGGEST_FIREWALL_CONFIG)); | 
 |     DCHECK(!(suggestions & SUGGEST_DNS_CONFIG)); | 
 |     DCHECK(!(suggestions & SUGGEST_SECURE_DNS_CONFIG)); | 
 |   } | 
 | #elif defined(OS_ANDROID) | 
 |   if (IsSuggested(suggestions, SUGGEST_SECURE_DNS_CONFIG)) { | 
 |     AddSingleEntryDictionaryToList( | 
 |         suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_SECURE_DNS_SUMMARY, false); | 
 |   } | 
 | #endif | 
 |  | 
 |   if (IsSuggested(suggestions, SUGGEST_OFFLINE_CHECKS)) { | 
 | #if defined(OS_ANDROID) || defined(OS_IOS) | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_TURN_OFF_AIRPLANE_SUMMARY, false); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_TURN_ON_DATA_SUMMARY, false); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECKING_SIGNAL_SUMMARY, false); | 
 | #else | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_HARDWARE_SUMMARY, false); | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_CHECK_WIFI_SUMMARY, false); | 
 | #endif | 
 |   } | 
 |  | 
 | // If the current platform has a directly accesible network diagnostics tool and | 
 | // the URL is valid add a suggestion. | 
 | #if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_WIN) || defined(OS_MAC) | 
 |   if (IsOnlySuggestion(suggestions, SUGGEST_DIAGNOSE_TOOL)) { | 
 |     int diagose_message_id = | 
 |         error_code == error_page::DNS_PROBE_FINISHED_NXDOMAIN | 
 |             ? IDS_ERRORPAGES_SUGGESTION_DIAGNOSE_CHECK_TYPO_STANDALONE | 
 |             : IDS_ERRORPAGES_SUGGESTION_DIAGNOSE_STANDALONE; | 
 |  | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |                                    diagose_message_id, false); | 
 |     return; | 
 |   } | 
 |   if (IsSuggested(suggestions, SUGGEST_DIAGNOSE_TOOL)) { | 
 |     AddSingleEntryDictionaryToList(suggestions_summary_list, "summary", | 
 |         IDS_ERRORPAGES_SUGGESTION_DIAGNOSE, false); | 
 |   } | 
 | #else | 
 |   DCHECK(!IsSuggested(suggestions, SUGGEST_DIAGNOSE_TOOL)); | 
 | #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_WIN) || defined(OS_MAC) | 
 |  | 
 |   // Add list prefix header. | 
 |   error_strings->SetString("suggestionsSummaryListHeader", | 
 |       l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_LIST_HEADER)); | 
 | } | 
 |  | 
 | // Creates a dictionary with "header" and "body" entries and adds it | 
 | // to a ListValue, Returns the dictionary to allow further items to be added. | 
 | base::DictionaryValue* AddSuggestionDetailDictionaryToList( | 
 |     base::ListValue* list, | 
 |     int header_message_id, | 
 |     int body_message_id, | 
 |     bool append_standard_menu_items) { | 
 |   base::DictionaryValue* suggestion_list_item; | 
 |   if (append_standard_menu_items) { | 
 |     suggestion_list_item = GetStandardMenuItemsText(); | 
 |   } else { | 
 |     suggestion_list_item = new base::DictionaryValue; | 
 |   } | 
 |  | 
 |   if (header_message_id) { | 
 |     suggestion_list_item->SetString("header", | 
 |         l10n_util::GetStringUTF16(header_message_id)); | 
 |   } | 
 |   if (body_message_id) { | 
 |     suggestion_list_item->SetString("body", | 
 |         l10n_util::GetStringUTF16(body_message_id)); | 
 |   } | 
 |   list->Append(base::WrapUnique(suggestion_list_item)); | 
 |   // |suggestion_list_item| is invalidated at this point, so it needs to be | 
 |   // reset. | 
 |   list->GetDictionary(list->GetSize() - 1, &suggestion_list_item); | 
 |   return suggestion_list_item; | 
 | } | 
 |  | 
 | // Certain suggestions have supporting details which get displayed under | 
 | // the "Details" button. | 
 | void AddSuggestionsDetails(int error_code, | 
 |                            base::DictionaryValue* error_strings, | 
 |                            int suggestions, | 
 |                            base::ListValue* suggestions_details) { | 
 |   if (suggestions & SUGGEST_CHECK_CONNECTION) { | 
 |     AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |           IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER, | 
 |           IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_BODY, false); | 
 |   } | 
 |  | 
 | #if !defined(OS_IOS) | 
 |   if (suggestions & SUGGEST_SECURE_DNS_CONFIG) { | 
 |     AddSuggestionDetailDictionaryToList( | 
 |         suggestions_details, IDS_ERRORPAGES_SUGGESTION_SECURE_DNS_CONFIG_HEADER, | 
 |         IDS_ERRORPAGES_SUGGESTION_SECURE_DNS_CONFIG_BODY, true); | 
 |   } | 
 | #endif | 
 |  | 
 | #if !defined(OS_ANDROID) && !defined(OS_IOS) | 
 |   if (suggestions & SUGGEST_DNS_CONFIG) { | 
 |     AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |           IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG_HEADER, | 
 |           IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG_BODY, false); | 
 |  | 
 |     base::DictionaryValue* suggest_network_prediction = | 
 |         AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |             IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION_HEADER, | 
 |             IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION_BODY, true); | 
 |  | 
 |     suggest_network_prediction->SetString( | 
 |         "noNetworkPredictionTitle", | 
 |         l10n_util::GetStringUTF16( | 
 |             IDS_NETWORK_PREDICTION_ENABLED_DESCRIPTION)); | 
 |   } | 
 |  | 
 |   if (suggestions & SUGGEST_FIREWALL_CONFIG) { | 
 |     AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |         IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG_HEADER, | 
 |         IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG_BODY, false); | 
 |   } | 
 |  | 
 |   if (suggestions & SUGGEST_PROXY_CONFIG) { | 
 |     base::DictionaryValue* suggest_proxy_config = | 
 |         AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |             IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG_HEADER, | 
 |             0, true); | 
 |  | 
 |     // Custom body string. | 
 |     suggest_proxy_config->SetString("body", | 
 |         l10n_util::GetStringFUTF16(IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG_BODY, | 
 |             l10n_util::GetStringUTF16( | 
 |                 IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM))); | 
 |  | 
 |     suggest_proxy_config->SetString("proxyTitle", | 
 |         l10n_util::GetStringUTF16(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON)); | 
 |   } | 
 | #endif | 
 |  | 
 |   if (suggestions & SUGGEST_CONTACT_ADMINISTRATOR && | 
 |       error_code == net::ERR_BLOCKED_BY_ADMINISTRATOR) { | 
 |     AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |         IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES_HEADER, | 
 |         IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES_BODY, false); | 
 |   } | 
 |  | 
 |   if (suggestions & SUGGEST_UNSUPPORTED_CIPHER) { | 
 |     AddSuggestionDetailDictionaryToList(suggestions_details, | 
 |         IDS_ERRORPAGES_SUGGESTION_UNSUPPORTED_CIPHER_HEADER, | 
 |         IDS_ERRORPAGES_SUGGESTION_UNSUPPORTED_CIPHER_BODY, false); | 
 |   } | 
 | } | 
 |  | 
 | std::string HttpErrorCodeToString(int error) { | 
 |   return std::string("HTTP ERROR ") + base::NumberToString(error); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | LocalizedError::PageState::PageState() = default; | 
 | LocalizedError::PageState::~PageState() = default; | 
 | LocalizedError::PageState::PageState(PageState&& other) = default; | 
 | LocalizedError::PageState& LocalizedError::PageState::operator=( | 
 |     PageState&& other) = default; | 
 |  | 
 | LocalizedError::PageState LocalizedError::GetPageState( | 
 |     int error_code, | 
 |     const std::string& error_domain, | 
 |     const GURL& failed_url, | 
 |     bool is_post, | 
 |     bool is_secure_dns_network_error, | 
 |     bool stale_copy_in_cache, | 
 |     bool can_show_network_diagnostics_dialog, | 
 |     bool is_incognito, | 
 |     bool offline_content_feature_enabled, | 
 |     bool auto_fetch_feature_enabled, | 
 |     bool is_kiosk_mode, | 
 |     const std::string& locale) { | 
 |   LocalizedError::PageState result; | 
 |   result.is_offline_error = IsOfflineError(error_domain, error_code); | 
 |  | 
 |   webui::SetLoadTimeDataDefaults(locale, &result.strings); | 
 |  | 
 |   bool show_game_instructions = failed_url.host() == kChromeUIDinoHost && | 
 |                                 failed_url.scheme() == kChromeUIScheme; | 
 |  | 
 |   // Grab the strings and settings that depend on the error type.  Init | 
 |   // options with default values. | 
 |   LocalizedErrorMap options = { | 
 |     0, | 
 |     IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, | 
 |     IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, | 
 |     SUGGEST_NONE, | 
 |     SHOW_NO_BUTTONS, | 
 |   }; | 
 |  | 
 |   const LocalizedErrorMap* error_map = LookupErrorMap( | 
 |       error_domain, error_code, is_secure_dns_network_error, is_post); | 
 |   if (error_map) | 
 |     options = *error_map; | 
 |  | 
 |   // If we got "access denied" but the url was a file URL, then we say it was a | 
 |   // file instead of just using the "not available" default message. Just adding | 
 |   // ERR_ACCESS_DENIED to the map isn't sufficient, since that message may be | 
 |   // generated by some OSs when the operation doesn't involve a file URL. | 
 |   if (error_domain == Error::kNetErrorDomain && | 
 |       error_code == net::ERR_ACCESS_DENIED && failed_url.scheme() == "file") { | 
 |     options.heading_resource_id = IDS_ERRORPAGES_HEADING_FILE_ACCESS_DENIED; | 
 |     options.summary_resource_id = IDS_ERRORPAGES_SUMMARY_FILE_ACCESS_DENIED; | 
 |     options.suggestions = SUGGEST_NONE; | 
 |     options.buttons = SHOW_BUTTON_RELOAD; | 
 |   } | 
 |  | 
 |   // Do not show any suggestions with links while in kiosk mode. | 
 |   if (is_kiosk_mode) { | 
 |     options.suggestions &= ~SUGGEST_DIAGNOSE_TOOL; | 
 |     options.suggestions &= ~SUGGEST_LEARNMORE; | 
 |   } | 
 |  | 
 |   base::string16 failed_url_string(url_formatter::FormatUrl( | 
 |       failed_url, url_formatter::kFormatUrlOmitNothing, | 
 |       net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr)); | 
 |   // URLs are always LTR. | 
 |   if (base::i18n::IsRTL()) | 
 |     base::i18n::WrapStringWithLTRFormatting(&failed_url_string); | 
 |  | 
 |   base::string16 host_name(url_formatter::IDNToUnicode(failed_url.host())); | 
 |   if (failed_url.SchemeIsHTTPOrHTTPS()) | 
 |     result.strings.SetString("title", host_name); | 
 |   else | 
 |     result.strings.SetString("title", failed_url_string); | 
 |  | 
 |   std::string icon_class = GetIconClassForError(error_domain, error_code); | 
 |   result.strings.SetString("iconClass", icon_class); | 
 |  | 
 |   auto heading = std::make_unique<base::DictionaryValue>(); | 
 |  | 
 |   int msg_id = show_game_instructions ? IDS_ERRORPAGES_GAME_INSTRUCTIONS | 
 |                                       : options.heading_resource_id; | 
 |   heading->SetString("msg", l10n_util::GetStringUTF16(msg_id)); | 
 |   heading->SetString("hostName", host_name); | 
 |   result.strings.Set("heading", std::move(heading)); | 
 |  | 
 |   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | 
 |   // Check if easter egg should be disabled. | 
 |   if (command_line->HasSwitch( | 
 |           error_page::switches::kDisableDinosaurEasterEgg)) { | 
 |     // The presence of this string disables the easter egg. Acts as a flag. | 
 |     result.strings.SetString( | 
 |         "disabledEasterEgg", | 
 |         l10n_util::GetStringUTF16(IDS_ERRORPAGE_FUN_DISABLED)); | 
 |   } | 
 |  | 
 |   // Return early and don't add suggestions or other information when showing | 
 |   // game instructions. | 
 |   if (show_game_instructions) { | 
 |     // When showing instructions, set an empty error to prevent a "NULL" string. | 
 |     result.strings.SetString("errorCode", ""); | 
 |     return result; | 
 |   } | 
 |  | 
 |   auto summary = std::make_unique<base::DictionaryValue>(); | 
 |  | 
 |   // Set summary message under the heading. | 
 |   summary->SetString("msg", | 
 |                      l10n_util::GetStringUTF16(options.summary_resource_id)); | 
 |   summary->SetString("failedUrl", failed_url_string); | 
 |   summary->SetString("hostName", host_name); | 
 |  | 
 |   result.strings.SetString( | 
 |       "details", l10n_util::GetStringUTF16(IDS_ERRORPAGE_NET_BUTTON_DETAILS)); | 
 |   result.strings.SetString( | 
 |       "hideDetails", | 
 |       l10n_util::GetStringUTF16(IDS_ERRORPAGE_NET_BUTTON_HIDE_DETAILS)); | 
 |   result.strings.Set("summary", std::move(summary)); | 
 |  | 
 |   base::string16 error_string; | 
 |   if (error_domain == Error::kNetErrorDomain) { | 
 |     // Non-internationalized error string, for debugging Chrome itself. | 
 |     error_string = base::ASCIIToUTF16(net::ErrorToShortString(error_code)); | 
 |   } else if (error_domain == Error::kDnsProbeErrorDomain) { | 
 |     std::string ascii_error_string = | 
 |         error_page::DnsProbeStatusToString(error_code); | 
 |     error_string = base::ASCIIToUTF16(ascii_error_string); | 
 |   } else { | 
 |     DCHECK_EQ(Error::kHttpErrorDomain, error_domain); | 
 |     error_string = base::ASCIIToUTF16(HttpErrorCodeToString(error_code)); | 
 |   } | 
 |   result.strings.SetString("errorCode", error_string); | 
 |  | 
 |   base::ListValue* suggestions_details = result.strings.SetList( | 
 |       "suggestionsDetails", std::make_unique<base::ListValue>()); | 
 |   base::ListValue* suggestions_summary_list = result.strings.SetList( | 
 |       "suggestionsSummaryList", std::make_unique<base::ListValue>()); | 
 |  | 
 |   // Add the reload suggestion, if needed, for pages that didn't come | 
 |   // from a post. | 
 |   if ((options.buttons & SHOW_BUTTON_RELOAD) && !is_post) { | 
 |     auto reload_button = std::make_unique<base::DictionaryValue>(); | 
 |     result.reload_button_shown = true; | 
 |     reload_button->SetString( | 
 |         "msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD)); | 
 |     reload_button->SetString("reloadUrl", failed_url.spec()); | 
 |     result.strings.Set("reloadButton", std::move(reload_button)); | 
 |   } | 
 |  | 
 | #if BUILDFLAG(IS_CHROMEOS_ASH) | 
 |   // ChromeOS has its own diagnostics extension, which doesn't rely on a | 
 |   // browser-initiated dialog. | 
 |   can_show_network_diagnostics_dialog = true; | 
 | #endif  // BUILDFLAG(IS_CHROMEOS_ASH) | 
 |  | 
 |   // Add default suggestions and any relevant supporting details. | 
 |   GetSuggestionsSummaryList(error_code, &result.strings, options.suggestions, | 
 |                             locale, suggestions_summary_list, | 
 |                             can_show_network_diagnostics_dialog, failed_url); | 
 |   AddSuggestionsDetails(error_code, &result.strings, options.suggestions, | 
 |                         suggestions_details); | 
 |  | 
 | #if defined(OS_ANDROID) | 
 |   if (!is_post && !result.reload_button_shown && !is_incognito && | 
 |       failed_url.is_valid() && failed_url.SchemeIsHTTPOrHTTPS() && | 
 |       IsOfflineError(error_domain, error_code)) { | 
 |     if (!auto_fetch_feature_enabled) { | 
 |       result.download_button_shown = true; | 
 |       result.strings.SetPath({"downloadButton", "msg"}, | 
 |                              base::Value(l10n_util::GetStringUTF16( | 
 |                                  IDS_ERRORPAGES_BUTTON_DOWNLOAD))); | 
 |       result.strings.SetPath({"downloadButton", "disabledMsg"}, | 
 |                              base::Value(l10n_util::GetStringUTF16( | 
 |                                  IDS_ERRORPAGES_BUTTON_DOWNLOADING))); | 
 |     } else { | 
 |       result.auto_fetch_allowed = true; | 
 |       result.strings.SetString("attemptAutoFetch", "true"); | 
 |       result.strings.SetPath({"savePageLater", "savePageMsg"}, | 
 |                              base::Value(l10n_util::GetStringUTF16( | 
 |                                  IDS_ERRORPAGES_SAVE_PAGE_BUTTON))); | 
 |       result.strings.SetPath({"savePageLater", "cancelMsg"}, | 
 |                              base::Value(l10n_util::GetStringUTF16( | 
 |                                  IDS_ERRORPAGES_CANCEL_SAVE_PAGE_BUTTON))); | 
 |     } | 
 |   } | 
 |  | 
 |   result.strings.SetString( | 
 |       "closeDescriptionPopup", | 
 |       l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_CLOSE_POPUP_BUTTON)); | 
 |  | 
 |   if (IsOfflineError(error_domain, error_code) && !is_incognito) { | 
 |     result.offline_content_feature_enabled = offline_content_feature_enabled; | 
 |     if (offline_content_feature_enabled) { | 
 |       result.strings.SetString("suggestedOfflineContentPresentation", "on"); | 
 |       result.strings.SetPath({"offlineContentList", "title"}, | 
 |                              base::Value(l10n_util::GetStringUTF16( | 
 |                                  IDS_ERRORPAGES_OFFLINE_CONTENT_LIST_TITLE))); | 
 |       result.strings.SetPath( | 
 |           {"offlineContentList", "actionText"}, | 
 |           base::Value(l10n_util::GetStringUTF16( | 
 |               IDS_ERRORPAGES_OFFLINE_CONTENT_LIST_OPEN_ALL_BUTTON))); | 
 |       result.strings.SetPath( | 
 |           {"offlineContentList", "showText"}, | 
 |           base::Value(l10n_util::GetStringUTF16(IDS_SHOW_CONTENT))); | 
 |       result.strings.SetPath( | 
 |           {"offlineContentList", "hideText"}, | 
 |           base::Value(l10n_util::GetStringUTF16(IDS_HIDE_CONTENT))); | 
 |     } | 
 |   } | 
 | #endif  // defined(OS_ANDROID) | 
 |   return result; | 
 | } | 
 |  | 
 | base::string16 LocalizedError::GetErrorDetails(const std::string& error_domain, | 
 |                                                int error_code, | 
 |                                                bool is_secure_dns_network_error, | 
 |                                                bool is_post) { | 
 |   const LocalizedErrorMap* error_map = LookupErrorMap( | 
 |       error_domain, error_code, is_secure_dns_network_error, is_post); | 
 |   if (error_map) | 
 |     return l10n_util::GetStringUTF16(error_map->summary_resource_id); | 
 |   else | 
 |     return l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE); | 
 | } | 
 |  | 
 | bool LocalizedError::HasStrings(const std::string& error_domain, | 
 |                                 int error_code) { | 
 |   // Whether or not the there are strings for an error does not depend on | 
 |   // whether or not the page was generated by a POST, so just claim it was | 
 |   // not. Likewise it does not depend on whether a DNS error resulted from a | 
 |   // secure DNS lookup or not. | 
 |   return LookupErrorMap(error_domain, error_code, | 
 |                         /*is_secure_dns_network_error=*/false, | 
 |                         /*is_post=*/false) != nullptr; | 
 | } | 
 |  | 
 | }  // namespace error_page |