• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/browser/net/net_error_tab_helper.h"
6 
7 #include "base/bind.h"
8 #include "base/prefs/pref_service.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/io_thread.h"
11 #include "chrome/browser/net/dns_probe_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/net/net_error_info.h"
14 #include "chrome/common/pref_names.h"
15 #include "chrome/common/render_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "net/base/net_errors.h"
18 
19 using chrome_common_net::DnsProbeStatus;
20 using chrome_common_net::DnsProbeStatusToString;
21 using content::BrowserContext;
22 using content::BrowserThread;
23 using content::PageTransition;
24 using content::RenderViewHost;
25 using content::WebContents;
26 using content::WebContentsObserver;
27 
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper);
29 
30 namespace chrome_browser_net {
31 
32 namespace {
33 
34 static NetErrorTabHelper::TestingState testing_state_ =
35     NetErrorTabHelper::TESTING_DEFAULT;
36 
37 // Returns whether |net_error| is a DNS-related error (and therefore whether
38 // the tab helper should start a DNS probe after receiving it.)
IsDnsError(int net_error)39 bool IsDnsError(int net_error) {
40   return net_error == net::ERR_NAME_NOT_RESOLVED ||
41          net_error == net::ERR_NAME_RESOLUTION_FAILED;
42 }
43 
OnDnsProbeFinishedOnIOThread(const base::Callback<void (DnsProbeStatus)> & callback,DnsProbeStatus result)44 void OnDnsProbeFinishedOnIOThread(
45     const base::Callback<void(DnsProbeStatus)>& callback,
46     DnsProbeStatus result) {
47   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
48 
49   BrowserThread::PostTask(
50       BrowserThread::UI,
51       FROM_HERE,
52       base::Bind(callback, result));
53 }
54 
55 // Can only access g_browser_process->io_thread() from the browser thread,
56 // so have to pass it in to the callback instead of dereferencing it here.
StartDnsProbeOnIOThread(const base::Callback<void (DnsProbeStatus)> & callback,IOThread * io_thread)57 void StartDnsProbeOnIOThread(
58     const base::Callback<void(DnsProbeStatus)>& callback,
59     IOThread* io_thread) {
60   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
61 
62   DnsProbeService* probe_service =
63       io_thread->globals()->dns_probe_service.get();
64 
65   probe_service->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread, callback));
66 }
67 
68 }  // namespace
69 
~NetErrorTabHelper()70 NetErrorTabHelper::~NetErrorTabHelper() {
71 }
72 
73 // static
set_state_for_testing(TestingState state)74 void NetErrorTabHelper::set_state_for_testing(TestingState state) {
75   testing_state_ = state;
76 }
77 
DidStartProvisionalLoadForFrame(int64 frame_id,int64 parent_frame_id,bool is_main_frame,const GURL & validated_url,bool is_error_page,bool is_iframe_srcdoc,RenderViewHost * render_view_host)78 void NetErrorTabHelper::DidStartProvisionalLoadForFrame(
79     int64 frame_id,
80     int64 parent_frame_id,
81     bool is_main_frame,
82     const GURL& validated_url,
83     bool is_error_page,
84     bool is_iframe_srcdoc,
85     RenderViewHost* render_view_host) {
86   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87 
88   if (!is_main_frame)
89     return;
90 
91   is_error_page_ = is_error_page;
92 }
93 
DidCommitProvisionalLoadForFrame(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & url,PageTransition transition_type,RenderViewHost * render_view_host)94 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
95     int64 frame_id,
96     const base::string16& frame_unique_name,
97     bool is_main_frame,
98     const GURL& url,
99     PageTransition transition_type,
100     RenderViewHost* render_view_host) {
101   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102 
103   if (!is_main_frame)
104     return;
105 
106   // Resend status every time an error page commits; this is somewhat spammy,
107   // but ensures that the status will make it to the real error page, even if
108   // the link doctor loads a blank intermediate page or the tab switches
109   // renderer processes.
110   if (is_error_page_ && dns_error_active_) {
111     dns_error_page_committed_ = true;
112     DVLOG(1) << "Committed error page; resending status.";
113     SendInfo();
114   } else {
115     dns_error_active_ = false;
116     dns_error_page_committed_ = false;
117   }
118 }
119 
DidFailProvisionalLoad(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & validated_url,int error_code,const base::string16 & error_description,RenderViewHost * render_view_host)120 void NetErrorTabHelper::DidFailProvisionalLoad(
121     int64 frame_id,
122     const base::string16& frame_unique_name,
123     bool is_main_frame,
124     const GURL& validated_url,
125     int error_code,
126     const base::string16& error_description,
127     RenderViewHost* render_view_host) {
128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
129 
130   if (!is_main_frame)
131     return;
132 
133   if (IsDnsError(error_code)) {
134     dns_error_active_ = true;
135     OnMainFrameDnsError();
136   }
137 }
138 
NetErrorTabHelper(WebContents * contents)139 NetErrorTabHelper::NetErrorTabHelper(WebContents* contents)
140     : WebContentsObserver(contents),
141       weak_factory_(this),
142       is_error_page_(false),
143       dns_error_active_(false),
144       dns_error_page_committed_(false),
145       dns_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE) {
146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147 
148   // If this helper is under test, it won't have a WebContents.
149   if (contents)
150     InitializePref(contents);
151 }
152 
OnMainFrameDnsError()153 void NetErrorTabHelper::OnMainFrameDnsError() {
154   if (ProbesAllowed()) {
155     // Don't start more than one probe at a time.
156     if (dns_probe_status_ != chrome_common_net::DNS_PROBE_STARTED) {
157       StartDnsProbe();
158       dns_probe_status_ = chrome_common_net::DNS_PROBE_STARTED;
159     }
160   } else {
161     dns_probe_status_ = chrome_common_net::DNS_PROBE_NOT_RUN;
162   }
163 }
164 
StartDnsProbe()165 void NetErrorTabHelper::StartDnsProbe() {
166   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
167   DCHECK(dns_error_active_);
168   DCHECK_NE(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
169 
170   DVLOG(1) << "Starting DNS probe.";
171 
172   BrowserThread::PostTask(
173       BrowserThread::IO,
174       FROM_HERE,
175       base::Bind(&StartDnsProbeOnIOThread,
176                  base::Bind(&NetErrorTabHelper::OnDnsProbeFinished,
177                             weak_factory_.GetWeakPtr()),
178                  g_browser_process->io_thread()));
179 }
180 
OnDnsProbeFinished(DnsProbeStatus result)181 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result) {
182   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183   DCHECK_EQ(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
184   DCHECK(chrome_common_net::DnsProbeStatusIsFinished(result));
185 
186   DVLOG(1) << "Finished DNS probe with result "
187            << DnsProbeStatusToString(result) << ".";
188 
189   dns_probe_status_ = result;
190 
191   if (dns_error_page_committed_)
192     SendInfo();
193 }
194 
InitializePref(WebContents * contents)195 void NetErrorTabHelper::InitializePref(WebContents* contents) {
196   DCHECK(contents);
197 
198   BrowserContext* browser_context = contents->GetBrowserContext();
199   Profile* profile = Profile::FromBrowserContext(browser_context);
200   resolve_errors_with_web_service_.Init(
201       prefs::kAlternateErrorPagesEnabled,
202       profile->GetPrefs());
203 }
204 
ProbesAllowed() const205 bool NetErrorTabHelper::ProbesAllowed() const {
206   if (testing_state_ != TESTING_DEFAULT)
207     return testing_state_ == TESTING_FORCE_ENABLED;
208 
209   // TODO(ttuttle): Disable on mobile?
210   return *resolve_errors_with_web_service_;
211 }
212 
SendInfo()213 void NetErrorTabHelper::SendInfo() {
214   DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, dns_probe_status_);
215   DCHECK(dns_error_page_committed_);
216 
217   DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_);
218   Send(new ChromeViewMsg_NetErrorInfo(routing_id(), dns_probe_status_));
219 
220   if (!dns_probe_status_snoop_callback_.is_null())
221     dns_probe_status_snoop_callback_.Run(dns_probe_status_);
222 }
223 
224 }  // namespace chrome_browser_net
225