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