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.
5 #include "chrome/renderer/net/net_error_helper.h"
9 #include "base/command_line.h"
10 #include "base/i18n/rtl.h"
11 #include "base/json/json_writer.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/localized_error.h"
17 #include "chrome/common/net/net_error_info.h"
18 #include "chrome/common/render_messages.h"
19 #include "chrome/grit/renderer_resources.h"
20 #include "chrome/renderer/net/net_error_page_controller.h"
21 #include "content/public/common/content_client.h"
22 #include "content/public/common/url_constants.h"
23 #include "content/public/renderer/content_renderer_client.h"
24 #include "content/public/renderer/document_state.h"
25 #include "content/public/renderer/render_frame.h"
26 #include "content/public/renderer/render_thread.h"
27 #include "content/public/renderer/render_view.h"
28 #include "content/public/renderer/resource_fetcher.h"
29 #include "ipc/ipc_message.h"
30 #include "ipc/ipc_message_macros.h"
31 #include "third_party/WebKit/public/platform/WebURL.h"
32 #include "third_party/WebKit/public/platform/WebURLError.h"
33 #include "third_party/WebKit/public/platform/WebURLRequest.h"
34 #include "third_party/WebKit/public/platform/WebURLResponse.h"
35 #include "third_party/WebKit/public/web/WebDataSource.h"
36 #include "third_party/WebKit/public/web/WebDocument.h"
37 #include "third_party/WebKit/public/web/WebFrame.h"
38 #include "third_party/WebKit/public/web/WebView.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/base/webui/jstemplate_builder.h"
43 using base::JSONWriter
;
44 using chrome_common_net::DnsProbeStatus
;
45 using chrome_common_net::DnsProbeStatusToString
;
46 using content::DocumentState
;
47 using content::RenderFrame
;
48 using content::RenderFrameObserver
;
49 using content::RenderThread
;
50 using content::kUnreachableWebDataURL
;
54 // Number of seconds to wait for the navigation correction service to return
55 // suggestions. If it takes too long, just use the local error page.
56 static const int kNavigationCorrectionFetchTimeoutSec
= 3;
58 NetErrorHelperCore::PageType
GetLoadingPageType(const blink::WebFrame
* frame
) {
59 GURL url
= frame
->provisionalDataSource()->request().url();
60 if (!url
.is_valid() || url
.spec() != kUnreachableWebDataURL
)
61 return NetErrorHelperCore::NON_ERROR_PAGE
;
62 return NetErrorHelperCore::ERROR_PAGE
;
65 NetErrorHelperCore::FrameType
GetFrameType(const blink::WebFrame
* frame
) {
67 return NetErrorHelperCore::MAIN_FRAME
;
68 return NetErrorHelperCore::SUB_FRAME
;
73 NetErrorHelper::NetErrorHelper(RenderFrame
* render_frame
)
74 : RenderFrameObserver(render_frame
),
75 content::RenderFrameObserverTracker
<NetErrorHelper
>(render_frame
) {
76 RenderThread::Get()->AddObserver(this);
77 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
78 bool auto_reload_enabled
=
79 command_line
->HasSwitch(switches::kEnableOfflineAutoReload
);
80 bool auto_reload_visible_only
=
81 command_line
->HasSwitch(switches::kEnableOfflineAutoReloadVisibleOnly
);
82 core_
.reset(new NetErrorHelperCore(this,
84 auto_reload_visible_only
,
85 !render_frame
->IsHidden()));
88 NetErrorHelper::~NetErrorHelper() {
89 RenderThread::Get()->RemoveObserver(this);
92 void NetErrorHelper::ReloadButtonPressed() {
93 core_
->ExecuteButtonPress(NetErrorHelperCore::RELOAD_BUTTON
);
96 void NetErrorHelper::LoadStaleButtonPressed() {
97 core_
->ExecuteButtonPress(NetErrorHelperCore::LOAD_STALE_BUTTON
);
100 void NetErrorHelper::MoreButtonPressed() {
101 core_
->ExecuteButtonPress(NetErrorHelperCore::MORE_BUTTON
);
104 void NetErrorHelper::DidStartProvisionalLoad() {
105 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
106 core_
->OnStartLoad(GetFrameType(frame
), GetLoadingPageType(frame
));
109 void NetErrorHelper::DidCommitProvisionalLoad(bool is_new_navigation
) {
110 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
111 core_
->OnCommitLoad(GetFrameType(frame
), frame
->document().url());
114 void NetErrorHelper::DidFinishLoad() {
115 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
116 core_
->OnFinishLoad(GetFrameType(frame
));
119 void NetErrorHelper::OnStop() {
123 void NetErrorHelper::WasShown() {
127 void NetErrorHelper::WasHidden() {
128 core_
->OnWasHidden();
131 bool NetErrorHelper::OnMessageReceived(const IPC::Message
& message
) {
134 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper
, message
)
135 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo
, OnNetErrorInfo
)
136 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetNavigationCorrectionInfo
,
137 OnSetNavigationCorrectionInfo
);
138 IPC_MESSAGE_UNHANDLED(handled
= false)
139 IPC_END_MESSAGE_MAP()
144 void NetErrorHelper::NetworkStateChanged(bool enabled
) {
145 core_
->NetworkStateChanged(enabled
);
148 void NetErrorHelper::GetErrorHTML(
149 blink::WebFrame
* frame
,
150 const blink::WebURLError
& error
,
152 std::string
* error_html
) {
153 core_
->GetErrorHTML(GetFrameType(frame
), error
, is_failed_post
, error_html
);
156 bool NetErrorHelper::ShouldSuppressErrorPage(blink::WebFrame
* frame
,
158 return core_
->ShouldSuppressErrorPage(GetFrameType(frame
), url
);
161 void NetErrorHelper::TrackClick(int tracking_id
) {
162 core_
->TrackClick(tracking_id
);
165 void NetErrorHelper::GenerateLocalizedErrorPage(
166 const blink::WebURLError
& error
,
168 scoped_ptr
<LocalizedError::ErrorPageParams
> params
,
169 bool* reload_button_shown
,
170 bool* load_stale_button_shown
,
171 std::string
* error_html
) const {
174 int resource_id
= IDR_NET_ERROR_HTML
;
175 const base::StringPiece
template_html(
176 ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id
));
177 if (template_html
.empty()) {
178 NOTREACHED() << "unable to load template.";
180 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
181 bool load_stale_cache_enabled
=
182 command_line
->HasSwitch(switches::kEnableOfflineLoadStaleCache
);
184 base::DictionaryValue error_strings
;
185 LocalizedError::GetStrings(error
.reason
, error
.domain
.utf8(),
186 error
.unreachableURL
, is_failed_post
,
187 (load_stale_cache_enabled
&&
188 error
.staleCopyInCache
&& !is_failed_post
),
189 RenderThread::Get()->GetLocale(),
190 render_frame()->GetRenderView()->
191 GetAcceptLanguages(),
192 params
.Pass(), &error_strings
);
193 *reload_button_shown
= error_strings
.Get("reloadButton", NULL
);
194 *load_stale_button_shown
= error_strings
.Get("staleLoadButton", NULL
);
196 // "t" is the id of the template's root node.
197 *error_html
= webui::GetTemplatesHtml(template_html
, &error_strings
, "t");
201 void NetErrorHelper::LoadErrorPageInMainFrame(const std::string
& html
,
202 const GURL
& failed_url
) {
203 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
206 blink::WebFrame
* frame
= web_view
->mainFrame();
207 frame
->loadHTMLString(html
, GURL(kUnreachableWebDataURL
), failed_url
, true);
210 void NetErrorHelper::EnablePageHelperFunctions() {
211 NetErrorPageController::Install(render_frame());
214 void NetErrorHelper::UpdateErrorPage(const blink::WebURLError
& error
,
215 bool is_failed_post
) {
216 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
217 bool load_stale_cache_enabled
=
218 command_line
->HasSwitch(switches::kEnableOfflineLoadStaleCache
);
220 base::DictionaryValue error_strings
;
221 LocalizedError::GetStrings(error
.reason
,
223 error
.unreachableURL
,
225 (load_stale_cache_enabled
&&
226 error
.staleCopyInCache
&& !is_failed_post
),
227 RenderThread::Get()->GetLocale(),
228 render_frame()->GetRenderView()->
229 GetAcceptLanguages(),
230 scoped_ptr
<LocalizedError::ErrorPageParams
>(),
234 JSONWriter::Write(&error_strings
, &json
);
236 std::string js
= "if (window.updateForDnsProbe) "
237 "updateForDnsProbe(" + json
+ ");";
239 if (!base::UTF8ToUTF16(js
.c_str(), js
.length(), &js16
)) {
244 render_frame()->ExecuteJavaScript(js16
);
247 void NetErrorHelper::FetchNavigationCorrections(
248 const GURL
& navigation_correction_url
,
249 const std::string
& navigation_correction_request_body
) {
250 DCHECK(!correction_fetcher_
.get());
252 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
255 blink::WebFrame
* frame
= web_view
->mainFrame();
257 correction_fetcher_
.reset(
258 content::ResourceFetcher::Create(navigation_correction_url
));
259 correction_fetcher_
->SetMethod("POST");
260 correction_fetcher_
->SetBody(navigation_correction_request_body
);
261 correction_fetcher_
->SetHeader("Content-Type", "application/json");
263 correction_fetcher_
->Start(
265 blink::WebURLRequest::RequestContextInternal
,
266 blink::WebURLRequest::FrameTypeTopLevel
,
267 base::Bind(&NetErrorHelper::OnNavigationCorrectionsFetched
,
268 base::Unretained(this)));
270 correction_fetcher_
->SetTimeout(
271 base::TimeDelta::FromSeconds(kNavigationCorrectionFetchTimeoutSec
));
274 void NetErrorHelper::CancelFetchNavigationCorrections() {
275 correction_fetcher_
.reset();
278 void NetErrorHelper::SendTrackingRequest(
279 const GURL
& tracking_url
,
280 const std::string
& tracking_request_body
) {
281 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
284 blink::WebFrame
* frame
= web_view
->mainFrame();
286 // If there's already a pending tracking request, this will cancel it.
287 tracking_fetcher_
.reset(content::ResourceFetcher::Create(tracking_url
));
288 tracking_fetcher_
->SetMethod("POST");
289 tracking_fetcher_
->SetBody(tracking_request_body
);
290 tracking_fetcher_
->SetHeader("Content-Type", "application/json");
292 tracking_fetcher_
->Start(
294 blink::WebURLRequest::RequestContextInternal
,
295 blink::WebURLRequest::FrameTypeTopLevel
,
296 base::Bind(&NetErrorHelper::OnTrackingRequestComplete
,
297 base::Unretained(this)));
300 void NetErrorHelper::ReloadPage() {
301 render_frame()->GetWebFrame()->reload(false);
304 void NetErrorHelper::LoadPageFromCache(const GURL
& page_url
) {
305 blink::WebFrame
* web_frame
= render_frame()->GetWebFrame();
306 DCHECK(!EqualsASCII(web_frame
->dataSource()->request().httpMethod(), "POST"));
308 blink::WebURLRequest
request(page_url
);
309 request
.setCachePolicy(blink::WebURLRequest::ReturnCacheDataDontLoad
);
311 web_frame
->loadRequest(request
);
314 void NetErrorHelper::OnNetErrorInfo(int status_num
) {
315 DCHECK(status_num
>= 0 && status_num
< chrome_common_net::DNS_PROBE_MAX
);
317 DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num
);
319 core_
->OnNetErrorInfo(static_cast<DnsProbeStatus
>(status_num
));
322 void NetErrorHelper::OnSetNavigationCorrectionInfo(
323 const GURL
& navigation_correction_url
,
324 const std::string
& language
,
325 const std::string
& country_code
,
326 const std::string
& api_key
,
327 const GURL
& search_url
) {
328 core_
->OnSetNavigationCorrectionInfo(navigation_correction_url
, language
,
329 country_code
, api_key
, search_url
);
332 void NetErrorHelper::OnNavigationCorrectionsFetched(
333 const blink::WebURLResponse
& response
,
334 const std::string
& data
) {
335 // The fetcher may only be deleted after |data| is passed to |core_|. Move
336 // it to a temporary to prevent any potential re-entrancy issues.
337 scoped_ptr
<content::ResourceFetcher
> fetcher(
338 correction_fetcher_
.release());
339 bool success
= (!response
.isNull() && response
.httpStatusCode() == 200);
340 core_
->OnNavigationCorrectionsFetched(
342 render_frame()->GetRenderView()->GetAcceptLanguages(),
343 base::i18n::IsRTL());
346 void NetErrorHelper::OnTrackingRequestComplete(
347 const blink::WebURLResponse
& response
,
348 const std::string
& data
) {
349 tracking_fetcher_
.reset();