OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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/renderer/net/net_error_helper.h" | 5 #include "chrome/renderer/net/net_error_helper.h" |
6 | 6 |
7 #include <string> | |
8 | |
9 #include "base/json/json_writer.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
7 #include "base/values.h" | 11 #include "base/values.h" |
8 #include "chrome/common/localized_error.h" | 12 #include "chrome/common/localized_error.h" |
9 #include "chrome/common/render_messages.h" | 13 #include "chrome/common/render_messages.h" |
10 #include "chrome/common/net/net_error_info.h" | 14 #include "chrome/common/net/net_error_info.h" |
11 #include "content/public/common/content_client.h" | 15 #include "content/public/common/content_client.h" |
12 #include "content/public/common/url_constants.h" | 16 #include "content/public/common/url_constants.h" |
13 #include "content/public/renderer/content_renderer_client.h" | 17 #include "content/public/renderer/content_renderer_client.h" |
14 #include "content/public/renderer/render_thread.h" | 18 #include "content/public/renderer/render_thread.h" |
15 #include "content/public/renderer/render_view.h" | 19 #include "content/public/renderer/render_view.h" |
16 #include "googleurl/src/gurl.h" | 20 #include "googleurl/src/gurl.h" |
17 #include "ipc/ipc_message.h" | 21 #include "ipc/ipc_message.h" |
18 #include "ipc/ipc_message_macros.h" | 22 #include "ipc/ipc_message_macros.h" |
19 #include "net/base/net_errors.h" | 23 #include "net/base/net_errors.h" |
20 #include "third_party/WebKit/public/platform/WebURL.h" | 24 #include "third_party/WebKit/public/platform/WebURL.h" |
21 #include "third_party/WebKit/public/platform/WebURLError.h" | |
22 #include "third_party/WebKit/public/platform/WebURLRequest.h" | 25 #include "third_party/WebKit/public/platform/WebURLRequest.h" |
23 #include "third_party/WebKit/public/web/WebDataSource.h" | 26 #include "third_party/WebKit/public/web/WebDataSource.h" |
24 #include "third_party/WebKit/public/web/WebFrame.h" | 27 #include "third_party/WebKit/public/web/WebFrame.h" |
25 | 28 |
26 using chrome_common_net::DnsProbeResult; | 29 using base::JSONWriter; |
30 using chrome_common_net::DnsProbeStatus; | |
31 using chrome_common_net::DnsProbeStatusIsFinished; | |
32 using chrome_common_net::DnsProbeStatusToString; | |
33 using chrome_common_net::DnsProbesEnabledByFieldTrial; | |
27 using content::RenderThread; | 34 using content::RenderThread; |
28 using content::RenderView; | 35 using content::RenderView; |
29 using content::RenderViewObserver; | 36 using content::RenderViewObserver; |
30 using content::kUnreachableWebDataURL; | 37 using content::kUnreachableWebDataURL; |
31 | 38 |
32 namespace { | 39 namespace { |
33 | 40 |
34 GURL GetProvisionallyLoadingURLFromWebFrame(WebKit::WebFrame* frame) { | 41 bool IsLoadingErrorPage(WebKit::WebFrame* frame) { |
35 return frame->provisionalDataSource()->request().url(); | 42 GURL url = frame->provisionalDataSource()->request().url(); |
43 return url.spec() == kUnreachableWebDataURL; | |
36 } | 44 } |
37 | 45 |
38 bool IsErrorPage(const GURL& url) { | 46 bool IsMainFrame(const WebKit::WebFrame* frame) { |
39 return (url.spec() == kUnreachableWebDataURL); | 47 return !frame->parent(); |
40 } | 48 } |
41 | 49 |
42 // Returns whether |net_error| is a DNS-related error (and therefore whether | 50 // Returns whether |net_error| is a DNS-related error (and therefore whether |
43 // the tab helper should start a DNS probe after receiving it.) | 51 // the tab helper should start a DNS probe after receiving it.) |
44 bool IsDnsError(int net_error) { | 52 bool IsDnsError(const WebKit::WebURLError& error) { |
45 return net_error == net::ERR_NAME_NOT_RESOLVED || | 53 return std::string(error.domain.utf8()) == net::kErrorDomain && |
46 net_error == net::ERR_NAME_RESOLUTION_FAILED; | 54 (error.reason == net::ERR_NAME_NOT_RESOLVED || |
47 } | 55 error.reason == net::ERR_NAME_RESOLUTION_FAILED); |
48 | |
49 NetErrorTracker::FrameType GetFrameType(WebKit::WebFrame* frame) { | |
50 return frame->parent() ? NetErrorTracker::FRAME_SUB | |
51 : NetErrorTracker::FRAME_MAIN; | |
52 } | |
53 | |
54 NetErrorTracker::PageType GetPageType(WebKit::WebFrame* frame) { | |
55 bool error_page = IsErrorPage(GetProvisionallyLoadingURLFromWebFrame(frame)); | |
56 return error_page ? NetErrorTracker::PAGE_ERROR | |
57 : NetErrorTracker::PAGE_NORMAL; | |
58 } | |
59 | |
60 NetErrorTracker::ErrorType GetErrorType(const WebKit::WebURLError& error) { | |
61 return IsDnsError(error.reason) ? NetErrorTracker::ERROR_DNS | |
62 : NetErrorTracker::ERROR_OTHER; | |
63 } | |
64 | |
65 // Converts a DNS probe result into a net error. Returns OK if the error page | |
66 // should not be changed from the original DNS error. | |
67 int DnsProbeResultToNetError(DnsProbeResult result) { | |
68 switch (result) { | |
69 case chrome_common_net::DNS_PROBE_UNKNOWN: | |
70 return net::OK; | |
71 case chrome_common_net::DNS_PROBE_NO_INTERNET: | |
72 // TODO(ttuttle): This is not the same error as when NCN returns this; | |
73 // ideally we should have two separate error codes for "no network" and | |
74 // "network with no internet". | |
75 return net::ERR_INTERNET_DISCONNECTED; | |
76 case chrome_common_net::DNS_PROBE_BAD_CONFIG: | |
77 // This is unspecific enough that we should still show the full DNS error | |
78 // page. | |
79 return net::OK; | |
80 case chrome_common_net::DNS_PROBE_NXDOMAIN: | |
81 return net::ERR_NAME_NOT_RESOLVED; | |
82 default: | |
83 NOTREACHED(); | |
84 return net::OK; | |
85 } | |
86 } | |
87 | |
88 WebKit::WebURLError NetErrorToWebURLError(int net_error) { | |
89 WebKit::WebURLError error; | |
90 error.domain = WebKit::WebString::fromUTF8(net::kErrorDomain); | |
91 error.reason = net_error; | |
92 return error; | |
93 } | 56 } |
94 | 57 |
95 } // namespace | 58 } // namespace |
96 | 59 |
97 NetErrorHelper::NetErrorHelper(RenderView* render_view) | 60 NetErrorHelper::NetErrorHelper(RenderView* render_view) |
98 : RenderViewObserver(render_view), | 61 : RenderViewObserver(render_view), |
99 tracker_(base::Bind(&NetErrorHelper::TrackerCallback, | 62 last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE), |
100 base::Unretained(this))), | 63 last_start_was_error_page_(false), |
101 dns_error_page_state_(NetErrorTracker::DNS_ERROR_PAGE_NONE), | 64 last_fail_was_dns_error_(false), |
102 updated_error_page_(false), | 65 forwarding_probe_results_(false), |
103 is_failed_post_(false) { | 66 is_failed_post_(false) { |
104 } | 67 } |
105 | 68 |
106 NetErrorHelper::~NetErrorHelper() { | 69 NetErrorHelper::~NetErrorHelper() { |
107 } | 70 } |
108 | 71 |
109 void NetErrorHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) { | 72 void NetErrorHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) { |
110 tracker_.OnStartProvisionalLoad(GetFrameType(frame), GetPageType(frame)); | 73 OnStartLoad(IsMainFrame(frame), IsLoadingErrorPage(frame)); |
111 } | 74 } |
112 | 75 |
113 void NetErrorHelper::DidFailProvisionalLoad(WebKit::WebFrame* frame, | 76 void NetErrorHelper::DidFailProvisionalLoad(WebKit::WebFrame* frame, |
114 const WebKit::WebURLError& error) { | 77 const WebKit::WebURLError& error) { |
115 WebKit::WebDataSource* data_source = frame->provisionalDataSource(); | 78 const bool main_frame = IsMainFrame(frame); |
116 const WebKit::WebURLRequest& failed_request = data_source->request(); | 79 const bool dns_error = IsDnsError(error); |
117 is_failed_post_ = EqualsASCII(failed_request.httpMethod(), "POST"); | 80 |
118 tracker_.OnFailProvisionalLoad(GetFrameType(frame), GetErrorType(error)); | 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 } | |
mmenke
2013/07/12 16:14:48
Hmm...we're not testing either this or the stuff w
Deprecated (see juliatuttle)
2013/07/12 17:42:49
I'm not sure what a good way to test this would be
mmenke
2013/07/12 18:02:12
We could just pass error and is_failed_post into O
Deprecated (see juliatuttle)
2013/07/12 20:55:55
That seems ugly, and it still wouldn't check the a
mmenke
2013/07/15 16:33:22
My concern isn't so much that we'd overwrite it, b
| |
119 } | 90 } |
120 | 91 |
121 void NetErrorHelper::DidCommitProvisionalLoad(WebKit::WebFrame* frame, | 92 void NetErrorHelper::DidCommitProvisionalLoad(WebKit::WebFrame* frame, |
122 bool is_new_navigation) { | 93 bool is_new_navigation) { |
123 tracker_.OnCommitProvisionalLoad(GetFrameType(frame)); | 94 OnCommitLoad(IsMainFrame(frame)); |
124 } | 95 } |
125 | 96 |
126 void NetErrorHelper::DidFinishLoad(WebKit::WebFrame* frame) { | 97 void NetErrorHelper::DidFinishLoad(WebKit::WebFrame* frame) { |
127 tracker_.OnFinishLoad(GetFrameType(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 // If the helper was forwarding probe results and a normal page (or a non-DNS | |
135 // error page) has committed, stop forwarding probe results, since the DNS | |
136 // error page is gone. | |
137 if (!last_start_was_error_page_ || !last_fail_was_dns_error_) | |
138 forwarding_probe_results_ = false; | |
mmenke
2013/07/12 16:14:48
Any reason not to just set forwarding to false her
mmenke
2013/07/12 16:14:48
BUG: Need "last_probe_status_ = chrome_common_net
Deprecated (see juliatuttle)
2013/07/12 17:42:49
We do that in OnFailLoad, when a DNS error occurs.
Deprecated (see juliatuttle)
2013/07/12 17:42:49
Hmm, I guess not. If we have two DNS error pages
mmenke
2013/07/12 18:02:12
Hmm...I even searched the page for that. How did
Deprecated (see juliatuttle)
2013/07/12 20:55:55
Added another test that repeats the "DNS_PROBE_FIN
| |
139 } | |
140 | |
141 void NetErrorHelper::OnFinishLoad(bool is_main_frame) { | |
142 DVLOG(1) << "OnFinishLoad(is_main_frame=" << is_main_frame << ")"; | |
143 | |
144 if (!is_main_frame) | |
145 return; | |
146 | |
147 // If a DNS error page just finished loading, start forwarding probe results | |
148 // to it. | |
149 forwarding_probe_results_ = | |
150 last_fail_was_dns_error_ && last_start_was_error_page_; | |
mmenke
2013/07/12 16:14:48
No known way to set this to false for the link doc
Deprecated (see juliatuttle)
2013/07/12 17:42:49
Nope. As far as I can tell, the loads are identic
| |
151 | |
152 if (forwarding_probe_results_ && | |
153 last_probe_status_ != chrome_common_net::DNS_PROBE_POSSIBLE) { | |
154 DVLOG(1) << "Error page finished loading; sending saved status."; | |
155 UpdateErrorPage(); | |
156 } | |
128 } | 157 } |
129 | 158 |
130 bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) { | 159 bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) { |
131 bool handled = true; | 160 bool handled = true; |
132 | 161 |
133 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message) | 162 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message) |
134 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo) | 163 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo) |
135 IPC_MESSAGE_UNHANDLED(handled = false) | 164 IPC_MESSAGE_UNHANDLED(handled = false) |
136 IPC_END_MESSAGE_MAP() | 165 IPC_END_MESSAGE_MAP() |
137 | 166 |
138 return handled; | 167 return handled; |
139 } | 168 } |
140 | 169 |
141 void NetErrorHelper::OnNetErrorInfo(int dns_probe_result) { | 170 bool NetErrorHelper::GetErrorStringsForDnsProbe( |
142 DVLOG(1) << "Received DNS probe result " << dns_probe_result; | 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; | |
143 | 178 |
144 if (dns_probe_result < 0 || | 179 if (!IsDnsError(error)) |
145 dns_probe_result >= chrome_common_net::DNS_PROBE_MAX) { | 180 return false; |
146 DLOG(WARNING) << "Ignoring DNS probe result: invalid result " | 181 |
147 << dns_probe_result; | 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(), | |
mmenke
2013/07/12 16:14:48
Hmm...between fail and commit, we'd actually displ
Deprecated (see juliatuttle)
2013/07/12 17:42:49
I don't think so?
Until the error page commits, w
mmenke
2013/07/15 16:33:22
A page fails with a DNS issue, we commit the page.
| |
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)) { | |
148 NOTREACHED(); | 228 NOTREACHED(); |
149 return; | 229 return; |
150 } | 230 } |
151 | 231 |
152 if (dns_error_page_state_ != NetErrorTracker::DNS_ERROR_PAGE_LOADED) { | 232 DVLOG(1) << "Updating error page with status " |
153 DVLOG(1) << "Ignoring DNS probe result: not on DNS error page."; | 233 << chrome_common_net::DnsProbeStatusToString(last_probe_status_); |
154 return; | 234 DVLOG(2) << "New strings: " << js; |
235 | |
236 string16 frame_xpath; | |
237 render_view()->EvaluateScript(frame_xpath, js16, 0, false); | |
238 } | |
239 | |
240 WebKit::WebURLError NetErrorHelper::GetUpdatedError() const { | |
241 // If a probe didn't run or wasn't conclusive, restore the original error. | |
242 if (last_probe_status_ == chrome_common_net::DNS_PROBE_NOT_RUN || | |
243 last_probe_status_ == chrome_common_net::DNS_PROBE_FINISHED_UNKNOWN) { | |
244 return last_error_; | |
155 } | 245 } |
156 | 246 |
157 if (updated_error_page_) { | 247 WebKit::WebURLError error; |
158 DVLOG(1) << "Ignoring DNS probe result: already updated error page."; | 248 error.domain = WebKit::WebString::fromUTF8( |
159 return; | 249 chrome_common_net::kDnsProbeErrorDomain); |
160 } | 250 error.reason = last_probe_status_; |
251 error.unreachableURL = last_error_.unreachableURL; | |
161 | 252 |
162 UpdateErrorPage(static_cast<DnsProbeResult>(dns_probe_result)); | 253 return error; |
163 updated_error_page_ = true; | |
164 } | 254 } |
165 | |
166 void NetErrorHelper::TrackerCallback( | |
167 NetErrorTracker::DnsErrorPageState state) { | |
168 dns_error_page_state_ = state; | |
169 | |
170 if (state == NetErrorTracker::DNS_ERROR_PAGE_LOADED) | |
171 updated_error_page_ = false; | |
172 } | |
173 | |
174 void NetErrorHelper::UpdateErrorPage(DnsProbeResult dns_probe_result) { | |
175 DVLOG(1) << "Updating error page with result " << dns_probe_result; | |
176 | |
177 int net_error = DnsProbeResultToNetError(dns_probe_result); | |
178 if (net_error == net::OK) | |
179 return; | |
180 | |
181 DVLOG(1) << "net error code is " << net_error; | |
182 | |
183 base::DictionaryValue error_strings; | |
184 LocalizedError::GetStrings(NetErrorToWebURLError(net_error), | |
185 is_failed_post_, | |
186 RenderThread::Get()->GetLocale(), | |
187 &error_strings); | |
188 | |
189 // TODO(ttuttle): Update error page with error_strings. | |
190 } | |
OLD | NEW |