Home | History | Annotate | Download | only in net
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/renderer/net/net_error_helper.h"
      6 
      7 #include <string>
      8 
      9 #include "base/json/json_writer.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "base/values.h"
     13 #include "chrome/common/localized_error.h"
     14 #include "chrome/common/net/net_error_info.h"
     15 #include "chrome/common/render_messages.h"
     16 #include "content/public/common/content_client.h"
     17 #include "content/public/common/url_constants.h"
     18 #include "content/public/renderer/content_renderer_client.h"
     19 #include "content/public/renderer/render_thread.h"
     20 #include "content/public/renderer/render_view.h"
     21 #include "ipc/ipc_message.h"
     22 #include "ipc/ipc_message_macros.h"
     23 #include "net/base/net_errors.h"
     24 #include "third_party/WebKit/public/platform/WebURL.h"
     25 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     26 #include "third_party/WebKit/public/web/WebDataSource.h"
     27 #include "third_party/WebKit/public/web/WebFrame.h"
     28 #include "url/gurl.h"
     29 
     30 using base::JSONWriter;
     31 using chrome_common_net::DnsProbeStatus;
     32 using chrome_common_net::DnsProbeStatusIsFinished;
     33 using chrome_common_net::DnsProbeStatusToString;
     34 using content::RenderThread;
     35 using content::RenderView;
     36 using content::RenderViewObserver;
     37 using content::kUnreachableWebDataURL;
     38 
     39 namespace {
     40 
     41 bool IsLoadingErrorPage(WebKit::WebFrame* frame) {
     42   GURL url = frame->provisionalDataSource()->request().url();
     43   return url.spec() == kUnreachableWebDataURL;
     44 }
     45 
     46 bool IsMainFrame(const WebKit::WebFrame* frame) {
     47   return !frame->parent();
     48 }
     49 
     50 // Returns whether |net_error| is a DNS-related error (and therefore whether
     51 // the tab helper should start a DNS probe after receiving it.)
     52 bool IsDnsError(const WebKit::WebURLError& error) {
     53   return std::string(error.domain.utf8()) == net::kErrorDomain &&
     54          (error.reason == net::ERR_NAME_NOT_RESOLVED ||
     55           error.reason == net::ERR_NAME_RESOLUTION_FAILED);
     56 }
     57 
     58 }  // namespace
     59 
     60 NetErrorHelper::NetErrorHelper(RenderView* render_view)
     61     : RenderViewObserver(render_view),
     62       last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
     63       last_start_was_error_page_(false),
     64       last_fail_was_dns_error_(false),
     65       forwarding_probe_results_(false),
     66       is_failed_post_(false) {
     67 }
     68 
     69 NetErrorHelper::~NetErrorHelper() {
     70 }
     71 
     72 void NetErrorHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) {
     73   OnStartLoad(IsMainFrame(frame), IsLoadingErrorPage(frame));
     74 }
     75 
     76 void NetErrorHelper::DidFailProvisionalLoad(WebKit::WebFrame* frame,
     77                                             const WebKit::WebURLError& error) {
     78   const bool main_frame = IsMainFrame(frame);
     79   const bool dns_error = IsDnsError(error);
     80 
     81   OnFailLoad(main_frame, dns_error);
     82 
     83   if (main_frame && dns_error) {
     84     last_error_ = error;
     85 
     86     WebKit::WebDataSource* data_source = frame->provisionalDataSource();
     87     const WebKit::WebURLRequest& failed_request = data_source->request();
     88     is_failed_post_ = EqualsASCII(failed_request.httpMethod(), "POST");
     89   }
     90 }
     91 
     92 void NetErrorHelper::DidCommitProvisionalLoad(WebKit::WebFrame* frame,
     93                                               bool is_new_navigation) {
     94   OnCommitLoad(IsMainFrame(frame));
     95 }
     96 
     97 void NetErrorHelper::DidFinishLoad(WebKit::WebFrame* frame) {
     98   OnFinishLoad(IsMainFrame(frame));
     99 }
    100 
    101 void NetErrorHelper::OnStartLoad(bool is_main_frame, bool is_error_page) {
    102   DVLOG(1) << "OnStartLoad(is_main_frame=" << is_main_frame
    103            << ", is_error_page=" << is_error_page << ")";
    104   if (!is_main_frame)
    105     return;
    106 
    107   last_start_was_error_page_ = is_error_page;
    108 }
    109 
    110 void NetErrorHelper::OnFailLoad(bool is_main_frame, bool is_dns_error) {
    111   DVLOG(1) << "OnFailLoad(is_main_frame=" << is_main_frame
    112            << ", is_dns_error=" << is_dns_error << ")";
    113 
    114   if (!is_main_frame)
    115     return;
    116 
    117   last_fail_was_dns_error_ = is_dns_error;
    118 
    119   if (is_dns_error) {
    120     last_probe_status_ = chrome_common_net::DNS_PROBE_POSSIBLE;
    121     // If the helper was forwarding probe results and another DNS error has
    122     // occurred, stop forwarding probe results until the corresponding (new)
    123     // error page loads.
    124     forwarding_probe_results_ = false;
    125   }
    126 }
    127 
    128 void NetErrorHelper::OnCommitLoad(bool is_main_frame) {
    129   DVLOG(1) << "OnCommitLoad(is_main_frame=" << is_main_frame << ")";
    130 
    131   if (!is_main_frame)
    132     return;
    133 
    134   // Stop forwarding results.  If the page is a DNS error page, forwarding
    135   // will resume once the page is loaded; if not, it should stay stopped until
    136   // the next DNS error page.
    137   forwarding_probe_results_ = false;
    138 }
    139 
    140 void NetErrorHelper::OnFinishLoad(bool is_main_frame) {
    141   DVLOG(1) << "OnFinishLoad(is_main_frame=" << is_main_frame << ")";
    142 
    143   if (!is_main_frame)
    144     return;
    145 
    146   // If a DNS error page just finished loading, start forwarding probe results
    147   // to it.
    148   forwarding_probe_results_ =
    149       last_fail_was_dns_error_ && last_start_was_error_page_;
    150 
    151   if (forwarding_probe_results_ &&
    152       last_probe_status_ != chrome_common_net::DNS_PROBE_POSSIBLE) {
    153     DVLOG(1) << "Error page finished loading; sending saved status.";
    154     UpdateErrorPage();
    155   }
    156 }
    157 
    158 bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) {
    159   bool handled = true;
    160 
    161   IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message)
    162     IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo)
    163     IPC_MESSAGE_UNHANDLED(handled = false)
    164   IPC_END_MESSAGE_MAP()
    165 
    166   return handled;
    167 }
    168 
    169 // static
    170 bool NetErrorHelper::GetErrorStringsForDnsProbe(
    171     WebKit::WebFrame* frame,
    172     const WebKit::WebURLError& error,
    173     bool is_failed_post,
    174     const std::string& locale,
    175     base::DictionaryValue* error_strings) {
    176   if (!IsMainFrame(frame))
    177     return false;
    178 
    179   if (!IsDnsError(error))
    180     return false;
    181 
    182   // Get the strings for a fake "DNS probe possible" error.
    183   WebKit::WebURLError fake_error;
    184   fake_error.domain = WebKit::WebString::fromUTF8(
    185       chrome_common_net::kDnsProbeErrorDomain);
    186   fake_error.reason = chrome_common_net::DNS_PROBE_POSSIBLE;
    187   fake_error.unreachableURL = error.unreachableURL;
    188   LocalizedError::GetStrings(
    189       fake_error, is_failed_post, locale, error_strings);
    190   return true;
    191 }
    192 
    193 void NetErrorHelper::OnNetErrorInfo(int status_num) {
    194   DCHECK(status_num >= 0 && status_num < chrome_common_net::DNS_PROBE_MAX);
    195 
    196   DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num);
    197 
    198   DnsProbeStatus status = static_cast<DnsProbeStatus>(status_num);
    199   DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, status);
    200 
    201   if (!(last_fail_was_dns_error_ || forwarding_probe_results_)) {
    202     DVLOG(1) << "Ignoring NetErrorInfo: no DNS error";
    203     return;
    204   }
    205 
    206   last_probe_status_ = status;
    207 
    208   if (forwarding_probe_results_)
    209     UpdateErrorPage();
    210 }
    211 
    212 void NetErrorHelper::UpdateErrorPage() {
    213   DCHECK(forwarding_probe_results_);
    214 
    215   base::DictionaryValue error_strings;
    216   LocalizedError::GetStrings(GetUpdatedError(),
    217                              is_failed_post_,
    218                              RenderThread::Get()->GetLocale(),
    219                              &error_strings);
    220 
    221   std::string json;
    222   JSONWriter::Write(&error_strings, &json);
    223 
    224   std::string js = "if (window.updateForDnsProbe) "
    225                    "updateForDnsProbe(" + json + ");";
    226   string16 js16;
    227   if (!UTF8ToUTF16(js.c_str(), js.length(), &js16)) {
    228     NOTREACHED();
    229     return;
    230   }
    231 
    232   DVLOG(1) << "Updating error page with status "
    233            << chrome_common_net::DnsProbeStatusToString(last_probe_status_);
    234   DVLOG(2) << "New strings: " << js;
    235 
    236   string16 frame_xpath;
    237   render_view()->EvaluateScript(frame_xpath, js16, 0, false);
    238 
    239   UMA_HISTOGRAM_ENUMERATION("DnsProbe.ErrorPageUpdateStatus",
    240                             last_probe_status_,
    241                             chrome_common_net::DNS_PROBE_MAX);
    242 }
    243 
    244 WebKit::WebURLError NetErrorHelper::GetUpdatedError() const {
    245   // If a probe didn't run or wasn't conclusive, restore the original error.
    246   if (last_probe_status_ == chrome_common_net::DNS_PROBE_NOT_RUN ||
    247       last_probe_status_ ==
    248           chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE) {
    249     return last_error_;
    250   }
    251 
    252   WebKit::WebURLError error;
    253   error.domain = WebKit::WebString::fromUTF8(
    254       chrome_common_net::kDnsProbeErrorDomain);
    255   error.reason = last_probe_status_;
    256   error.unreachableURL = last_error_.unreachableURL;
    257 
    258   return error;
    259 }
    260