- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / renderer / net / net_error_helper.cc
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   if (!url.is_valid())
44     return false;
45   return url.spec() == kUnreachableWebDataURL;
46 }
47
48 bool IsMainFrame(const WebKit::WebFrame* frame) {
49   return !frame->parent();
50 }
51
52 // Returns whether |net_error| is a DNS-related error (and therefore whether
53 // the tab helper should start a DNS probe after receiving it.)
54 bool IsDnsError(const WebKit::WebURLError& error) {
55   return std::string(error.domain.utf8()) == net::kErrorDomain &&
56          (error.reason == net::ERR_NAME_NOT_RESOLVED ||
57           error.reason == net::ERR_NAME_RESOLUTION_FAILED);
58 }
59
60 }  // namespace
61
62 NetErrorHelper::NetErrorHelper(RenderView* render_view)
63     : RenderViewObserver(render_view),
64       last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
65       last_start_was_error_page_(false),
66       last_fail_was_dns_error_(false),
67       forwarding_probe_results_(false),
68       is_failed_post_(false) {
69 }
70
71 NetErrorHelper::~NetErrorHelper() {
72 }
73
74 void NetErrorHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) {
75   OnStartLoad(IsMainFrame(frame), IsLoadingErrorPage(frame));
76 }
77
78 void NetErrorHelper::DidFailProvisionalLoad(WebKit::WebFrame* frame,
79                                             const WebKit::WebURLError& error) {
80   const bool main_frame = IsMainFrame(frame);
81   const bool dns_error = IsDnsError(error);
82
83   OnFailLoad(main_frame, dns_error);
84
85   if (main_frame && dns_error) {
86     last_error_ = error;
87
88     WebKit::WebDataSource* data_source = frame->provisionalDataSource();
89     const WebKit::WebURLRequest& failed_request = data_source->request();
90     is_failed_post_ = EqualsASCII(failed_request.httpMethod(), "POST");
91   }
92 }
93
94 void NetErrorHelper::DidCommitProvisionalLoad(WebKit::WebFrame* frame,
95                                               bool is_new_navigation) {
96   OnCommitLoad(IsMainFrame(frame));
97 }
98
99 void NetErrorHelper::DidFinishLoad(WebKit::WebFrame* frame) {
100   OnFinishLoad(IsMainFrame(frame));
101 }
102
103 void NetErrorHelper::OnStartLoad(bool is_main_frame, bool is_error_page) {
104   DVLOG(1) << "OnStartLoad(is_main_frame=" << is_main_frame
105            << ", is_error_page=" << is_error_page << ")";
106   if (!is_main_frame)
107     return;
108
109   last_start_was_error_page_ = is_error_page;
110 }
111
112 void NetErrorHelper::OnFailLoad(bool is_main_frame, bool is_dns_error) {
113   DVLOG(1) << "OnFailLoad(is_main_frame=" << is_main_frame
114            << ", is_dns_error=" << is_dns_error << ")";
115
116   if (!is_main_frame)
117     return;
118
119   last_fail_was_dns_error_ = is_dns_error;
120
121   if (is_dns_error) {
122     last_probe_status_ = chrome_common_net::DNS_PROBE_POSSIBLE;
123     // If the helper was forwarding probe results and another DNS error has
124     // occurred, stop forwarding probe results until the corresponding (new)
125     // error page loads.
126     forwarding_probe_results_ = false;
127   }
128 }
129
130 void NetErrorHelper::OnCommitLoad(bool is_main_frame) {
131   DVLOG(1) << "OnCommitLoad(is_main_frame=" << is_main_frame << ")";
132
133   if (!is_main_frame)
134     return;
135
136   // Stop forwarding results.  If the page is a DNS error page, forwarding
137   // will resume once the page is loaded; if not, it should stay stopped until
138   // the next DNS error page.
139   forwarding_probe_results_ = false;
140 }
141
142 void NetErrorHelper::OnFinishLoad(bool is_main_frame) {
143   DVLOG(1) << "OnFinishLoad(is_main_frame=" << is_main_frame << ")";
144
145   if (!is_main_frame)
146     return;
147
148   // If a DNS error page just finished loading, start forwarding probe results
149   // to it.
150   forwarding_probe_results_ =
151       last_fail_was_dns_error_ && last_start_was_error_page_;
152
153   if (forwarding_probe_results_ &&
154       last_probe_status_ != chrome_common_net::DNS_PROBE_POSSIBLE) {
155     DVLOG(1) << "Error page finished loading; sending saved status.";
156     UpdateErrorPage();
157   }
158 }
159
160 bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) {
161   bool handled = true;
162
163   IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message)
164     IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo)
165     IPC_MESSAGE_UNHANDLED(handled = false)
166   IPC_END_MESSAGE_MAP()
167
168   return handled;
169 }
170
171 // static
172 bool NetErrorHelper::GetErrorStringsForDnsProbe(
173     WebKit::WebFrame* frame,
174     const WebKit::WebURLError& error,
175     bool is_failed_post,
176     const std::string& locale,
177     const std::string& accept_languages,
178     base::DictionaryValue* error_strings) {
179   if (!IsMainFrame(frame))
180     return false;
181
182   if (!IsDnsError(error))
183     return false;
184
185   // Get the strings for a fake "DNS probe possible" error.
186   LocalizedError::GetStrings(
187       chrome_common_net::DNS_PROBE_POSSIBLE,
188       chrome_common_net::kDnsProbeErrorDomain,
189       error.unreachableURL,
190       is_failed_post, locale, accept_languages, error_strings);
191   return true;
192 }
193
194 void NetErrorHelper::OnNetErrorInfo(int status_num) {
195   DCHECK(status_num >= 0 && status_num < chrome_common_net::DNS_PROBE_MAX);
196
197   DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num);
198
199   DnsProbeStatus status = static_cast<DnsProbeStatus>(status_num);
200   DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, status);
201
202   if (!(last_fail_was_dns_error_ || forwarding_probe_results_)) {
203     DVLOG(1) << "Ignoring NetErrorInfo: no DNS error";
204     return;
205   }
206
207   last_probe_status_ = status;
208
209   if (forwarding_probe_results_)
210     UpdateErrorPage();
211 }
212
213 void NetErrorHelper::UpdateErrorPage() {
214   DCHECK(forwarding_probe_results_);
215
216   WebKit::WebURLError error = GetUpdatedError();
217   base::DictionaryValue error_strings;
218   LocalizedError::GetStrings(error.reason,
219                              error.domain.utf8(),
220                              error.unreachableURL,
221                              is_failed_post_,
222                              RenderThread::Get()->GetLocale(),
223                              render_view()->GetAcceptLanguages(),
224                              &error_strings);
225
226   std::string json;
227   JSONWriter::Write(&error_strings, &json);
228
229   std::string js = "if (window.updateForDnsProbe) "
230                    "updateForDnsProbe(" + json + ");";
231   string16 js16;
232   if (!UTF8ToUTF16(js.c_str(), js.length(), &js16)) {
233     NOTREACHED();
234     return;
235   }
236
237   DVLOG(1) << "Updating error page with status "
238            << chrome_common_net::DnsProbeStatusToString(last_probe_status_);
239   DVLOG(2) << "New strings: " << js;
240
241   string16 frame_xpath;
242   render_view()->EvaluateScript(frame_xpath, js16, 0, false);
243
244   UMA_HISTOGRAM_ENUMERATION("DnsProbe.ErrorPageUpdateStatus",
245                             last_probe_status_,
246                             chrome_common_net::DNS_PROBE_MAX);
247 }
248
249 WebKit::WebURLError NetErrorHelper::GetUpdatedError() const {
250   // If a probe didn't run or wasn't conclusive, restore the original error.
251   if (last_probe_status_ == chrome_common_net::DNS_PROBE_NOT_RUN ||
252       last_probe_status_ ==
253           chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE) {
254     return last_error_;
255   }
256
257   WebKit::WebURLError error;
258   error.domain = WebKit::WebString::fromUTF8(
259       chrome_common_net::kDnsProbeErrorDomain);
260   error.reason = last_probe_status_;
261   error.unreachableURL = last_error_.unreachableURL;
262
263   return error;
264 }