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/render_messages.h"
18 #include "chrome/grit/renderer_resources.h"
19 #include "chrome/renderer/net/net_error_page_controller.h"
20 #include "components/error_page/common/error_page_params.h"
21 #include "components/error_page/common/net_error_info.h"
22 #include "content/public/common/content_client.h"
23 #include "content/public/common/url_constants.h"
24 #include "content/public/renderer/content_renderer_client.h"
25 #include "content/public/renderer/document_state.h"
26 #include "content/public/renderer/render_frame.h"
27 #include "content/public/renderer/render_thread.h"
28 #include "content/public/renderer/render_view.h"
29 #include "content/public/renderer/resource_fetcher.h"
30 #include "ipc/ipc_message.h"
31 #include "ipc/ipc_message_macros.h"
32 #include "third_party/WebKit/public/platform/WebURL.h"
33 #include "third_party/WebKit/public/platform/WebURLError.h"
34 #include "third_party/WebKit/public/platform/WebURLRequest.h"
35 #include "third_party/WebKit/public/platform/WebURLResponse.h"
36 #include "third_party/WebKit/public/web/WebDataSource.h"
37 #include "third_party/WebKit/public/web/WebDocument.h"
38 #include "third_party/WebKit/public/web/WebLocalFrame.h"
39 #include "third_party/WebKit/public/web/WebView.h"
40 #include "ui/base/resource/resource_bundle.h"
41 #include "ui/base/webui/jstemplate_builder.h"
44 using base::JSONWriter
;
45 using chrome_common_net::DnsProbeStatus
;
46 using chrome_common_net::DnsProbeStatusToString
;
47 using content::DocumentState
;
48 using content::RenderFrame
;
49 using content::RenderFrameObserver
;
50 using content::RenderThread
;
51 using content::kUnreachableWebDataURL
;
52 using error_page::ErrorPageParams
;
53 using error_page::NetErrorHelperCore
;
57 // Number of seconds to wait for the navigation correction service to return
58 // suggestions. If it takes too long, just use the local error page.
59 static const int kNavigationCorrectionFetchTimeoutSec
= 3;
61 NetErrorHelperCore::PageType
GetLoadingPageType(const blink::WebFrame
* frame
) {
62 GURL url
= frame
->provisionalDataSource()->request().url();
63 if (!url
.is_valid() || url
.spec() != kUnreachableWebDataURL
)
64 return NetErrorHelperCore::NON_ERROR_PAGE
;
65 return NetErrorHelperCore::ERROR_PAGE
;
68 NetErrorHelperCore::FrameType
GetFrameType(const blink::WebFrame
* frame
) {
70 return NetErrorHelperCore::MAIN_FRAME
;
71 return NetErrorHelperCore::SUB_FRAME
;
76 NetErrorHelper::NetErrorHelper(RenderFrame
* render_frame
)
77 : RenderFrameObserver(render_frame
),
78 content::RenderFrameObserverTracker
<NetErrorHelper
>(render_frame
) {
79 RenderThread::Get()->AddObserver(this);
80 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
81 bool auto_reload_enabled
=
82 command_line
->HasSwitch(switches::kEnableOfflineAutoReload
);
83 bool auto_reload_visible_only
=
84 command_line
->HasSwitch(switches::kEnableOfflineAutoReloadVisibleOnly
);
85 core_
.reset(new NetErrorHelperCore(this,
87 auto_reload_visible_only
,
88 !render_frame
->IsHidden()));
91 NetErrorHelper::~NetErrorHelper() {
92 RenderThread::Get()->RemoveObserver(this);
95 void NetErrorHelper::ReloadButtonPressed() {
96 core_
->ExecuteButtonPress(NetErrorHelperCore::RELOAD_BUTTON
);
99 void NetErrorHelper::LoadStaleButtonPressed() {
100 core_
->ExecuteButtonPress(NetErrorHelperCore::LOAD_STALE_BUTTON
);
103 void NetErrorHelper::MoreButtonPressed() {
104 core_
->ExecuteButtonPress(NetErrorHelperCore::MORE_BUTTON
);
107 void NetErrorHelper::DidStartProvisionalLoad() {
108 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
109 core_
->OnStartLoad(GetFrameType(frame
), GetLoadingPageType(frame
));
112 void NetErrorHelper::DidCommitProvisionalLoad(bool is_new_navigation
) {
113 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
114 core_
->OnCommitLoad(GetFrameType(frame
), frame
->document().url());
117 void NetErrorHelper::DidFinishLoad() {
118 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
119 core_
->OnFinishLoad(GetFrameType(frame
));
122 void NetErrorHelper::OnStop() {
126 void NetErrorHelper::WasShown() {
130 void NetErrorHelper::WasHidden() {
131 core_
->OnWasHidden();
134 bool NetErrorHelper::OnMessageReceived(const IPC::Message
& message
) {
137 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper
, message
)
138 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo
, OnNetErrorInfo
)
139 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetNavigationCorrectionInfo
,
140 OnSetNavigationCorrectionInfo
);
141 IPC_MESSAGE_UNHANDLED(handled
= false)
142 IPC_END_MESSAGE_MAP()
147 void NetErrorHelper::NetworkStateChanged(bool enabled
) {
148 core_
->NetworkStateChanged(enabled
);
151 void NetErrorHelper::GetErrorHTML(
152 blink::WebFrame
* frame
,
153 const blink::WebURLError
& error
,
155 std::string
* error_html
) {
156 core_
->GetErrorHTML(GetFrameType(frame
), error
, is_failed_post
, error_html
);
159 bool NetErrorHelper::ShouldSuppressErrorPage(blink::WebFrame
* frame
,
161 return core_
->ShouldSuppressErrorPage(GetFrameType(frame
), url
);
164 void NetErrorHelper::TrackClick(int tracking_id
) {
165 core_
->TrackClick(tracking_id
);
168 void NetErrorHelper::GenerateLocalizedErrorPage(
169 const blink::WebURLError
& error
,
171 scoped_ptr
<ErrorPageParams
> params
,
172 bool* reload_button_shown
,
173 bool* load_stale_button_shown
,
174 std::string
* error_html
) const {
177 int resource_id
= IDR_NET_ERROR_HTML
;
178 const base::StringPiece
template_html(
179 ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id
));
180 if (template_html
.empty()) {
181 NOTREACHED() << "unable to load template.";
183 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
184 bool load_stale_cache_enabled
=
185 command_line
->HasSwitch(switches::kEnableOfflineLoadStaleCache
);
187 base::DictionaryValue error_strings
;
188 LocalizedError::GetStrings(error
.reason
, error
.domain
.utf8(),
189 error
.unreachableURL
, is_failed_post
,
190 (load_stale_cache_enabled
&&
191 error
.staleCopyInCache
&& !is_failed_post
),
192 RenderThread::Get()->GetLocale(),
193 render_frame()->GetRenderView()->
194 GetAcceptLanguages(),
195 params
.Pass(), &error_strings
);
196 *reload_button_shown
= error_strings
.Get("reloadButton", NULL
);
197 *load_stale_button_shown
= error_strings
.Get("staleLoadButton", NULL
);
199 // "t" is the id of the template's root node.
200 *error_html
= webui::GetTemplatesHtml(template_html
, &error_strings
, "t");
204 void NetErrorHelper::LoadErrorPageInMainFrame(const std::string
& html
,
205 const GURL
& failed_url
) {
206 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
209 blink::WebFrame
* frame
= web_view
->mainFrame();
210 frame
->loadHTMLString(html
, GURL(kUnreachableWebDataURL
), failed_url
, true);
213 void NetErrorHelper::EnablePageHelperFunctions() {
214 NetErrorPageController::Install(render_frame());
217 void NetErrorHelper::UpdateErrorPage(const blink::WebURLError
& error
,
218 bool is_failed_post
) {
219 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
220 bool load_stale_cache_enabled
=
221 command_line
->HasSwitch(switches::kEnableOfflineLoadStaleCache
);
223 base::DictionaryValue error_strings
;
224 LocalizedError::GetStrings(error
.reason
,
226 error
.unreachableURL
,
228 (load_stale_cache_enabled
&&
229 error
.staleCopyInCache
&& !is_failed_post
),
230 RenderThread::Get()->GetLocale(),
231 render_frame()->GetRenderView()->
232 GetAcceptLanguages(),
233 scoped_ptr
<ErrorPageParams
>(),
237 JSONWriter::Write(&error_strings
, &json
);
239 std::string js
= "if (window.updateForDnsProbe) "
240 "updateForDnsProbe(" + json
+ ");";
242 if (!base::UTF8ToUTF16(js
.c_str(), js
.length(), &js16
)) {
247 render_frame()->ExecuteJavaScript(js16
);
250 void NetErrorHelper::FetchNavigationCorrections(
251 const GURL
& navigation_correction_url
,
252 const std::string
& navigation_correction_request_body
) {
253 DCHECK(!correction_fetcher_
.get());
255 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
258 blink::WebFrame
* frame
= web_view
->mainFrame();
260 correction_fetcher_
.reset(
261 content::ResourceFetcher::Create(navigation_correction_url
));
262 correction_fetcher_
->SetMethod("POST");
263 correction_fetcher_
->SetBody(navigation_correction_request_body
);
264 correction_fetcher_
->SetHeader("Content-Type", "application/json");
266 correction_fetcher_
->Start(
268 blink::WebURLRequest::RequestContextInternal
,
269 blink::WebURLRequest::FrameTypeTopLevel
,
270 content::ResourceFetcher::PLATFORM_LOADER
,
271 base::Bind(&NetErrorHelper::OnNavigationCorrectionsFetched
,
272 base::Unretained(this)));
274 correction_fetcher_
->SetTimeout(
275 base::TimeDelta::FromSeconds(kNavigationCorrectionFetchTimeoutSec
));
278 void NetErrorHelper::CancelFetchNavigationCorrections() {
279 correction_fetcher_
.reset();
282 void NetErrorHelper::SendTrackingRequest(
283 const GURL
& tracking_url
,
284 const std::string
& tracking_request_body
) {
285 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
288 blink::WebFrame
* frame
= web_view
->mainFrame();
290 // If there's already a pending tracking request, this will cancel it.
291 tracking_fetcher_
.reset(content::ResourceFetcher::Create(tracking_url
));
292 tracking_fetcher_
->SetMethod("POST");
293 tracking_fetcher_
->SetBody(tracking_request_body
);
294 tracking_fetcher_
->SetHeader("Content-Type", "application/json");
296 tracking_fetcher_
->Start(
298 blink::WebURLRequest::RequestContextInternal
,
299 blink::WebURLRequest::FrameTypeTopLevel
,
300 content::ResourceFetcher::PLATFORM_LOADER
,
301 base::Bind(&NetErrorHelper::OnTrackingRequestComplete
,
302 base::Unretained(this)));
305 void NetErrorHelper::ReloadPage() {
306 render_frame()->GetWebFrame()->reload(false);
309 void NetErrorHelper::LoadPageFromCache(const GURL
& page_url
) {
310 blink::WebFrame
* web_frame
= render_frame()->GetWebFrame();
311 DCHECK(!EqualsASCII(web_frame
->dataSource()->request().httpMethod(), "POST"));
313 blink::WebURLRequest
request(page_url
);
314 request
.setCachePolicy(blink::WebURLRequest::ReturnCacheDataDontLoad
);
316 web_frame
->loadRequest(request
);
319 void NetErrorHelper::OnNetErrorInfo(int status_num
) {
320 DCHECK(status_num
>= 0 && status_num
< chrome_common_net::DNS_PROBE_MAX
);
322 DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num
);
324 core_
->OnNetErrorInfo(static_cast<DnsProbeStatus
>(status_num
));
327 void NetErrorHelper::OnSetNavigationCorrectionInfo(
328 const GURL
& navigation_correction_url
,
329 const std::string
& language
,
330 const std::string
& country_code
,
331 const std::string
& api_key
,
332 const GURL
& search_url
) {
333 core_
->OnSetNavigationCorrectionInfo(navigation_correction_url
, language
,
334 country_code
, api_key
, search_url
);
337 void NetErrorHelper::OnNavigationCorrectionsFetched(
338 const blink::WebURLResponse
& response
,
339 const std::string
& data
) {
340 // The fetcher may only be deleted after |data| is passed to |core_|. Move
341 // it to a temporary to prevent any potential re-entrancy issues.
342 scoped_ptr
<content::ResourceFetcher
> fetcher(
343 correction_fetcher_
.release());
344 bool success
= (!response
.isNull() && response
.httpStatusCode() == 200);
345 core_
->OnNavigationCorrectionsFetched(
347 render_frame()->GetRenderView()->GetAcceptLanguages(),
348 base::i18n::IsRTL());
351 void NetErrorHelper::OnTrackingRequestComplete(
352 const blink::WebURLResponse
& response
,
353 const std::string
& data
) {
354 tracking_fetcher_
.reset();