Chromium Code Reviews| 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/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/Source/WebKit/chromium/public/WebDataSource.h" | 26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" |
| 24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | 27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
| 25 | 28 |
| 26 using base::DictionaryValue; | 29 using base::DictionaryValue; |
| 27 using chrome_common_net::DnsProbeResult; | 30 using base::JSONWriter; |
| 31 using chrome_common_net::DnsProbeStatus; | |
| 32 using chrome_common_net::DnsProbeStatusIsFinished; | |
| 33 using chrome_common_net::DnsProbeStatusToString; | |
| 34 using chrome_common_net::DnsProbesEnabledByFieldTrial; | |
| 28 using content::RenderThread; | 35 using content::RenderThread; |
| 29 using content::RenderView; | 36 using content::RenderView; |
| 30 using content::RenderViewObserver; | 37 using content::RenderViewObserver; |
| 31 using content::kUnreachableWebDataURL; | 38 using content::kUnreachableWebDataURL; |
| 32 | 39 |
| 33 namespace { | 40 namespace { |
| 34 | 41 |
| 35 GURL GetProvisionallyLoadingURLFromWebFrame(WebKit::WebFrame* frame) { | 42 bool IsLoadingErrorPage(WebKit::WebFrame* frame) { |
| 36 return frame->provisionalDataSource()->request().url(); | 43 GURL url = frame->provisionalDataSource()->request().url(); |
| 44 return url.spec() == kUnreachableWebDataURL; | |
| 37 } | 45 } |
| 38 | 46 |
| 39 bool IsErrorPage(const GURL& url) { | 47 bool IsMainFrame(const WebKit::WebFrame* frame) { |
| 40 return (url.spec() == kUnreachableWebDataURL); | 48 return !frame->parent(); |
| 41 } | 49 } |
| 42 | 50 |
| 43 // Returns whether |net_error| is a DNS-related error (and therefore whether | 51 // Returns whether |net_error| is a DNS-related error (and therefore whether |
| 44 // the tab helper should start a DNS probe after receiving it.) | 52 // the tab helper should start a DNS probe after receiving it.) |
| 45 bool IsDnsError(int net_error) { | 53 bool IsDnsError(const WebKit::WebURLError& error) { |
| 46 return net_error == net::ERR_NAME_NOT_RESOLVED || | 54 return std::string(error.domain.utf8()) == net::kErrorDomain && |
| 47 net_error == net::ERR_NAME_RESOLUTION_FAILED; | 55 (error.reason == net::ERR_NAME_NOT_RESOLVED || |
| 48 } | 56 error.reason == net::ERR_NAME_RESOLUTION_FAILED); |
| 49 | |
| 50 NetErrorTracker::FrameType GetFrameType(WebKit::WebFrame* frame) { | |
| 51 return frame->parent() ? NetErrorTracker::FRAME_SUB | |
| 52 : NetErrorTracker::FRAME_MAIN; | |
| 53 } | |
| 54 | |
| 55 NetErrorTracker::PageType GetPageType(WebKit::WebFrame* frame) { | |
| 56 bool error_page = IsErrorPage(GetProvisionallyLoadingURLFromWebFrame(frame)); | |
| 57 return error_page ? NetErrorTracker::PAGE_ERROR | |
| 58 : NetErrorTracker::PAGE_NORMAL; | |
| 59 } | |
| 60 | |
| 61 NetErrorTracker::ErrorType GetErrorType(const WebKit::WebURLError& error) { | |
| 62 return IsDnsError(error.reason) ? NetErrorTracker::ERROR_DNS | |
| 63 : NetErrorTracker::ERROR_OTHER; | |
| 64 } | |
| 65 | |
| 66 // Converts a DNS probe result into a net error. Returns OK if the error page | |
| 67 // should not be changed from the original DNS error. | |
| 68 int DnsProbeResultToNetError(DnsProbeResult result) { | |
| 69 switch (result) { | |
| 70 case chrome_common_net::DNS_PROBE_UNKNOWN: | |
| 71 return net::OK; | |
| 72 case chrome_common_net::DNS_PROBE_NO_INTERNET: | |
| 73 // TODO(ttuttle): This is not the same error as when NCN returns this; | |
| 74 // ideally we should have two separate error codes for "no network" and | |
| 75 // "network with no internet". | |
| 76 return net::ERR_INTERNET_DISCONNECTED; | |
| 77 case chrome_common_net::DNS_PROBE_BAD_CONFIG: | |
| 78 // This is unspecific enough that we should still show the full DNS error | |
| 79 // page. | |
| 80 return net::OK; | |
| 81 case chrome_common_net::DNS_PROBE_NXDOMAIN: | |
| 82 return net::ERR_NAME_NOT_RESOLVED; | |
| 83 default: | |
| 84 NOTREACHED(); | |
| 85 return net::OK; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 WebKit::WebURLError NetErrorToWebURLError(int net_error) { | |
| 90 WebKit::WebURLError error; | |
| 91 error.domain = WebKit::WebString::fromUTF8(net::kErrorDomain); | |
| 92 error.reason = net_error; | |
| 93 return error; | |
| 94 } | 57 } |
| 95 | 58 |
| 96 } // namespace | 59 } // namespace |
| 97 | 60 |
| 98 NetErrorHelper::NetErrorHelper(RenderView* render_view) | 61 NetErrorHelper::NetErrorHelper(RenderView* render_view) |
| 99 : RenderViewObserver(render_view), | 62 : RenderViewObserver(render_view), |
| 100 tracker_(base::Bind(&NetErrorHelper::TrackerCallback, | 63 last_status_(chrome_common_net::DNS_PROBE_POSSIBLE), |
| 101 base::Unretained(this))), | 64 dns_error_active_(false), |
| 102 dns_error_page_state_(NetErrorTracker::DNS_ERROR_PAGE_NONE), | 65 page_loaded_(false), |
| 103 updated_error_page_(false), | |
| 104 is_failed_post_(false) { | 66 is_failed_post_(false) { |
| 105 } | 67 } |
| 106 | 68 |
| 107 NetErrorHelper::~NetErrorHelper() { | 69 NetErrorHelper::~NetErrorHelper() { |
| 108 } | 70 } |
| 109 | 71 |
| 110 void NetErrorHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) { | 72 void NetErrorHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) { |
| 111 tracker_.OnStartProvisionalLoad(GetFrameType(frame), GetPageType(frame)); | 73 OnStartLoad(IsMainFrame(frame), IsLoadingErrorPage(frame)); |
| 112 } | 74 } |
| 113 | 75 |
| 114 void NetErrorHelper::DidFailProvisionalLoad(WebKit::WebFrame* frame, | 76 void NetErrorHelper::DidFailProvisionalLoad(WebKit::WebFrame* frame, |
| 115 const WebKit::WebURLError& error) { | 77 const WebKit::WebURLError& error) { |
| 116 WebKit::WebDataSource* data_source = frame->provisionalDataSource(); | 78 const bool main_frame = IsMainFrame(frame); |
| 117 const WebKit::WebURLRequest& failed_request = data_source->request(); | 79 const bool dns_error = IsDnsError(error); |
| 118 is_failed_post_ = EqualsASCII(failed_request.httpMethod(), "POST"); | 80 |
| 119 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 } | |
| 120 } | 90 } |
| 121 | 91 |
| 122 void NetErrorHelper::DidCommitProvisionalLoad(WebKit::WebFrame* frame, | 92 void NetErrorHelper::DidCommitProvisionalLoad(WebKit::WebFrame* frame, |
| 123 bool is_new_navigation) { | 93 bool is_new_navigation) { |
| 124 tracker_.OnCommitProvisionalLoad(GetFrameType(frame)); | 94 OnCommitLoad(IsMainFrame(frame)); |
| 125 } | 95 } |
| 126 | 96 |
| 127 void NetErrorHelper::DidFinishLoad(WebKit::WebFrame* frame) { | 97 void NetErrorHelper::DidFinishLoad(WebKit::WebFrame* frame) { |
| 128 tracker_.OnFinishLoad(GetFrameType(frame)); | 98 OnFinishLoad(IsMainFrame(frame)); |
| 99 } | |
| 100 | |
| 101 void NetErrorHelper::OnStartLoad(bool is_main_frame, bool is_error_page) { | |
| 102 if (!is_main_frame) | |
| 103 return; | |
| 104 | |
| 105 is_error_page_ = is_error_page; | |
| 106 } | |
| 107 | |
| 108 void NetErrorHelper::OnFailLoad(bool is_main_frame, bool is_dns_error) { | |
| 109 if (!is_main_frame || !is_dns_error) | |
| 110 return; | |
| 111 | |
| 112 dns_error_active_ = true; | |
|
mmenke
2013/06/13 20:04:55
I think the state machine here is too confusing.
Deprecated (see juliatuttle)
2013/06/18 19:36:37
Done.
I had to clear page_loaded in OnFailLoad to
| |
| 113 last_status_ = chrome_common_net::DNS_PROBE_POSSIBLE; | |
| 114 } | |
| 115 | |
| 116 void NetErrorHelper::OnCommitLoad(bool is_main_frame) { | |
| 117 if (!is_main_frame) | |
| 118 return; | |
| 119 | |
| 120 if (!is_error_page_) | |
| 121 dns_error_active_ = false; | |
| 122 | |
| 123 page_loaded_ = false; | |
| 124 } | |
| 125 | |
| 126 void NetErrorHelper::OnFinishLoad(bool is_main_frame) { | |
|
mmenke
2013/06/13 20:04:55
This should check if it's a main frame, presumably
Deprecated (see juliatuttle)
2013/06/18 19:36:37
Done.
| |
| 127 page_loaded_ = true; | |
| 128 | |
| 129 if (dns_error_active_ && is_error_page_) { | |
| 130 DVLOG(1) << "Error page finished loading; updating."; | |
| 131 UpdateErrorPage(); | |
| 132 } | |
| 129 } | 133 } |
| 130 | 134 |
| 131 bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) { | 135 bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) { |
| 132 bool handled = true; | 136 bool handled = true; |
| 133 | 137 |
| 134 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message) | 138 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message) |
| 135 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo) | 139 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo) |
| 136 IPC_MESSAGE_UNHANDLED(handled = false) | 140 IPC_MESSAGE_UNHANDLED(handled = false) |
| 137 IPC_END_MESSAGE_MAP() | 141 IPC_END_MESSAGE_MAP() |
| 138 | 142 |
| 139 return handled; | 143 return handled; |
| 140 } | 144 } |
| 141 | 145 |
| 142 void NetErrorHelper::OnNetErrorInfo(int dns_probe_result) { | 146 bool NetErrorHelper::GetErrorStringsForDnsProbe( |
| 143 DVLOG(1) << "Received DNS probe result " << dns_probe_result; | 147 WebKit::WebFrame* frame, |
| 148 const WebKit::WebURLError& error, | |
| 149 bool is_failed_post, | |
| 150 const std::string& locale, | |
| 151 base::DictionaryValue* error_strings) { | |
| 152 if (!IsMainFrame(frame)) | |
| 153 return false; | |
| 144 | 154 |
| 145 if (dns_probe_result < 0 || | 155 if (!IsDnsError(error)) |
| 146 dns_probe_result >= chrome_common_net::DNS_PROBE_MAX) { | 156 return false; |
| 147 DLOG(WARNING) << "Ignoring DNS probe result: invalid result " | 157 |
| 148 << dns_probe_result; | 158 // Get the strings for a fake "DNS probe possible" error. |
| 159 WebKit::WebURLError fake_error; | |
| 160 fake_error.domain = WebKit::WebString::fromUTF8( | |
| 161 chrome_common_net::kDnsProbeErrorDomain); | |
| 162 fake_error.reason = chrome_common_net::DNS_PROBE_POSSIBLE; | |
| 163 fake_error.unreachableURL = error.unreachableURL; | |
| 164 LocalizedError::GetStrings( | |
| 165 fake_error, is_failed_post, locale, error_strings); | |
| 166 return true; | |
| 167 } | |
| 168 | |
| 169 void NetErrorHelper::OnNetErrorInfo(int status_num) { | |
| 170 if (status_num < 0 || | |
| 171 status_num >= chrome_common_net::DNS_PROBE_MAX) { | |
|
mmenke
2013/06/13 20:04:55
Think we can just DCHECK on this, with the branch
Deprecated (see juliatuttle)
2013/06/18 19:36:37
Done.
| |
| 172 LOG(WARNING) << "Ignoring NetErrorInfo: invalid status " << status_num; | |
| 149 NOTREACHED(); | 173 NOTREACHED(); |
| 150 return; | 174 return; |
| 151 } | 175 } |
| 152 | 176 |
| 153 if (dns_error_page_state_ != NetErrorTracker::DNS_ERROR_PAGE_LOADED) { | 177 DVLOG(1) << "Received status " |
| 154 DVLOG(1) << "Ignoring DNS probe result: not on DNS error page."; | 178 << DnsProbeStatusToString(status_num); |
| 179 | |
| 180 DnsProbeStatus status = static_cast<DnsProbeStatus>(status_num); | |
| 181 if (status == chrome_common_net::DNS_PROBE_POSSIBLE) { | |
|
mmenke
2013/06/13 20:04:55
Same as above comment - I think a DCHECK is suffic
Deprecated (see juliatuttle)
2013/06/18 19:36:37
Done.
| |
| 182 NOTREACHED(); | |
| 155 return; | 183 return; |
| 156 } | 184 } |
| 157 | 185 |
| 158 if (updated_error_page_) { | 186 if (!dns_error_active_) { |
| 159 DVLOG(1) << "Ignoring DNS probe result: already updated error page."; | 187 DVLOG(1) << "Ignoring NetErrorInfo: no active DNS error"; |
| 160 return; | 188 return; |
| 161 } | 189 } |
| 162 | 190 |
| 163 UpdateErrorPage(static_cast<DnsProbeResult>(dns_probe_result)); | 191 last_status_ = status; |
| 164 updated_error_page_ = true; | 192 |
| 193 if (page_loaded_) | |
| 194 UpdateErrorPage(); | |
|
mmenke
2013/06/13 20:04:55
Can this happen:
page_loaded_ starts as true (aft
Deprecated (see juliatuttle)
2013/06/18 19:36:37
...ha, that is the exact race condition I just fou
| |
| 165 } | 195 } |
| 166 | 196 |
| 167 void NetErrorHelper::TrackerCallback( | 197 void NetErrorHelper::UpdateErrorPage() { |
| 168 NetErrorTracker::DnsErrorPageState state) { | |
| 169 dns_error_page_state_ = state; | |
| 170 | |
| 171 if (state == NetErrorTracker::DNS_ERROR_PAGE_LOADED) | |
| 172 updated_error_page_ = false; | |
| 173 } | |
| 174 | |
| 175 void NetErrorHelper::UpdateErrorPage(DnsProbeResult dns_probe_result) { | |
| 176 DVLOG(1) << "Updating error page with result " << dns_probe_result; | |
| 177 | |
| 178 int net_error = DnsProbeResultToNetError(dns_probe_result); | |
| 179 if (net_error == net::OK) | |
| 180 return; | |
| 181 | |
| 182 DVLOG(1) << "net error code is " << net_error; | |
| 183 | |
| 184 DictionaryValue error_strings; | 198 DictionaryValue error_strings; |
| 185 LocalizedError::GetStrings(NetErrorToWebURLError(net_error), | 199 LocalizedError::GetStrings(GetUpdatedError(), |
| 186 is_failed_post_, | 200 is_failed_post_, |
| 187 RenderThread::Get()->GetLocale(), | 201 RenderThread::Get()->GetLocale(), |
| 188 &error_strings); | 202 &error_strings); |
| 189 | 203 |
| 190 // TODO(ttuttle): Update error page with error_strings. | 204 std::string json; |
| 205 JSONWriter::Write(&error_strings, &json); | |
| 206 | |
| 207 std::string js = "if (window.updateForDnsProbe) " | |
| 208 "updateForDnsProbe(" + json + ");"; | |
| 209 string16 js16; | |
| 210 if (!UTF8ToUTF16(js.c_str(), js.length(), &js16)) { | |
| 211 NOTREACHED(); | |
| 212 return; | |
| 213 } | |
| 214 | |
| 215 DVLOG(1) << "Updating error page with status " | |
| 216 << chrome_common_net::DnsProbeStatusToString(last_status_); | |
| 217 DVLOG(2) << "New strings: " << js; | |
| 218 | |
| 219 string16 frame_xpath; | |
| 220 render_view()->EvaluateScript(frame_xpath, js16, 0, false); | |
|
mmenke
2013/06/13 20:04:55
Should we do something to prevent too many updates
Deprecated (see juliatuttle)
2013/06/18 19:36:37
We could? I don't think it'd be a huge deal if it
| |
| 191 } | 221 } |
| 222 | |
| 223 WebKit::WebURLError NetErrorHelper::GetUpdatedError() const { | |
| 224 // If a probe didn't run or wasn't conclusive, restore the original error. | |
| 225 if (last_status_ == chrome_common_net::DNS_PROBE_NOT_RUN || | |
| 226 last_status_ == chrome_common_net::DNS_PROBE_FINISHED_UNKNOWN) { | |
| 227 return last_error_; | |
| 228 } | |
| 229 | |
| 230 WebKit::WebURLError error; | |
| 231 error.domain = WebKit::WebString::fromUTF8( | |
| 232 chrome_common_net::kDnsProbeErrorDomain); | |
| 233 error.reason = last_status_; | |
| 234 error.unreachableURL = last_error_.unreachableURL; | |
| 235 | |
| 236 return error; | |
| 237 } | |
| OLD | NEW |