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 "components/error_page/common/error_page_params.h"
20 #include "components/error_page/common/net_error_info.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/WebLocalFrame.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 content::DocumentState
;
45 using content::RenderFrame
;
46 using content::RenderFrameObserver
;
47 using content::RenderThread
;
48 using content::kUnreachableWebDataURL
;
49 using error_page::DnsProbeStatus
;
50 using error_page::DnsProbeStatusToString
;
51 using error_page::ErrorPageParams
;
52 using error_page::NetErrorHelperCore
;
56 // Number of seconds to wait for the navigation correction service to return
57 // suggestions. If it takes too long, just use the local error page.
58 const int kNavigationCorrectionFetchTimeoutSec
= 3;
60 NetErrorHelperCore::PageType
GetLoadingPageType(const blink::WebFrame
* frame
) {
61 GURL url
= frame
->provisionalDataSource()->request().url();
62 if (!url
.is_valid() || url
.spec() != kUnreachableWebDataURL
)
63 return NetErrorHelperCore::NON_ERROR_PAGE
;
64 return NetErrorHelperCore::ERROR_PAGE
;
67 NetErrorHelperCore::FrameType
GetFrameType(const blink::WebFrame
* frame
) {
69 return NetErrorHelperCore::MAIN_FRAME
;
70 return NetErrorHelperCore::SUB_FRAME
;
75 NetErrorHelper::NetErrorHelper(RenderFrame
* render_frame
)
76 : RenderFrameObserver(render_frame
),
77 content::RenderFrameObserverTracker
<NetErrorHelper
>(render_frame
),
78 weak_controller_delegate_factory_(this) {
79 RenderThread::Get()->AddObserver(this);
80 base::CommandLine
* command_line
= base::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::ButtonPressed(
96 error_page::NetErrorHelperCore::Button button
) {
97 core_
->ExecuteButtonPress(button
);
100 void NetErrorHelper::TrackClick(int tracking_id
) {
101 core_
->TrackClick(tracking_id
);
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 bool is_same_page_navigation
) {
111 // Invalidate weak pointers from old error page controllers. If loading a new
112 // error page, the controller has not yet been attached, so this won't affect
114 weak_controller_delegate_factory_
.InvalidateWeakPtrs();
116 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
117 core_
->OnCommitLoad(GetFrameType(frame
), frame
->document().url());
120 void NetErrorHelper::DidFinishLoad() {
121 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
122 core_
->OnFinishLoad(GetFrameType(frame
));
125 void NetErrorHelper::OnStop() {
129 void NetErrorHelper::WasShown() {
133 void NetErrorHelper::WasHidden() {
134 core_
->OnWasHidden();
137 bool NetErrorHelper::OnMessageReceived(const IPC::Message
& message
) {
140 IPC_BEGIN_MESSAGE_MAP(NetErrorHelper
, message
)
141 IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo
, OnNetErrorInfo
)
142 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetCanShowNetworkDiagnosticsDialog
,
143 OnSetCanShowNetworkDiagnosticsDialog
);
144 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetNavigationCorrectionInfo
,
145 OnSetNavigationCorrectionInfo
);
146 IPC_MESSAGE_UNHANDLED(handled
= false)
147 IPC_END_MESSAGE_MAP()
152 void NetErrorHelper::NetworkStateChanged(bool enabled
) {
153 core_
->NetworkStateChanged(enabled
);
156 void NetErrorHelper::GetErrorHTML(
157 blink::WebFrame
* frame
,
158 const blink::WebURLError
& error
,
160 std::string
* error_html
) {
161 core_
->GetErrorHTML(GetFrameType(frame
), error
, is_failed_post
, error_html
);
164 bool NetErrorHelper::ShouldSuppressErrorPage(blink::WebFrame
* frame
,
166 return core_
->ShouldSuppressErrorPage(GetFrameType(frame
), url
);
169 void NetErrorHelper::GenerateLocalizedErrorPage(
170 const blink::WebURLError
& error
,
172 bool can_show_network_diagnostics_dialog
,
173 scoped_ptr
<ErrorPageParams
> params
,
174 bool* reload_button_shown
,
175 bool* show_saved_copy_button_shown
,
176 bool* show_cached_copy_button_shown
,
177 std::string
* error_html
) const {
180 int resource_id
= IDR_NET_ERROR_HTML
;
181 const base::StringPiece
template_html(
182 ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id
));
183 if (template_html
.empty()) {
184 NOTREACHED() << "unable to load template.";
186 base::DictionaryValue error_strings
;
187 LocalizedError::GetStrings(error
.reason
, error
.domain
.utf8(),
188 error
.unreachableURL
, is_failed_post
,
189 error
.staleCopyInCache
,
190 can_show_network_diagnostics_dialog
,
191 RenderThread::Get()->GetLocale(),
192 render_frame()->GetRenderView()->
193 GetAcceptLanguages(),
194 params
.Pass(), &error_strings
);
195 *reload_button_shown
= error_strings
.Get("reloadButton", nullptr);
196 *show_saved_copy_button_shown
=
197 error_strings
.Get("showSavedCopyButton", nullptr);
198 *show_cached_copy_button_shown
=
199 error_strings
.Get("cacheButton", nullptr);
200 // "t" is the id of the template's root node.
201 *error_html
= webui::GetTemplatesHtml(template_html
, &error_strings
, "t");
205 void NetErrorHelper::LoadErrorPageInMainFrame(const std::string
& html
,
206 const GURL
& failed_url
) {
207 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
210 blink::WebFrame
* frame
= web_view
->mainFrame();
211 frame
->loadHTMLString(html
, GURL(kUnreachableWebDataURL
), failed_url
, true);
214 void NetErrorHelper::EnablePageHelperFunctions() {
215 NetErrorPageController::Install(
216 render_frame(), weak_controller_delegate_factory_
.GetWeakPtr());
219 void NetErrorHelper::UpdateErrorPage(const blink::WebURLError
& error
,
221 bool can_show_network_diagnostics_dialog
) {
222 base::DictionaryValue error_strings
;
223 LocalizedError::GetStrings(error
.reason
,
225 error
.unreachableURL
,
227 error
.staleCopyInCache
,
228 can_show_network_diagnostics_dialog
,
229 RenderThread::Get()->GetLocale(),
230 render_frame()->GetRenderView()->
231 GetAcceptLanguages(),
232 scoped_ptr
<ErrorPageParams
>(),
236 JSONWriter::Write(error_strings
, &json
);
238 std::string js
= "if (window.updateForDnsProbe) "
239 "updateForDnsProbe(" + json
+ ");";
241 if (!base::UTF8ToUTF16(js
.c_str(), js
.length(), &js16
)) {
246 render_frame()->ExecuteJavaScript(js16
);
249 void NetErrorHelper::FetchNavigationCorrections(
250 const GURL
& navigation_correction_url
,
251 const std::string
& navigation_correction_request_body
) {
252 DCHECK(!correction_fetcher_
.get());
254 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
257 blink::WebFrame
* frame
= web_view
->mainFrame();
259 correction_fetcher_
.reset(
260 content::ResourceFetcher::Create(navigation_correction_url
));
261 correction_fetcher_
->SetMethod("POST");
262 correction_fetcher_
->SetBody(navigation_correction_request_body
);
263 correction_fetcher_
->SetHeader("Content-Type", "application/json");
265 correction_fetcher_
->Start(
267 blink::WebURLRequest::RequestContextInternal
,
268 blink::WebURLRequest::FrameTypeNone
,
269 content::ResourceFetcher::PLATFORM_LOADER
,
270 base::Bind(&NetErrorHelper::OnNavigationCorrectionsFetched
,
271 base::Unretained(this)));
273 correction_fetcher_
->SetTimeout(
274 base::TimeDelta::FromSeconds(kNavigationCorrectionFetchTimeoutSec
));
277 void NetErrorHelper::CancelFetchNavigationCorrections() {
278 correction_fetcher_
.reset();
281 void NetErrorHelper::SendTrackingRequest(
282 const GURL
& tracking_url
,
283 const std::string
& tracking_request_body
) {
284 blink::WebView
* web_view
= render_frame()->GetRenderView()->GetWebView();
287 blink::WebFrame
* frame
= web_view
->mainFrame();
289 // If there's already a pending tracking request, this will cancel it.
290 tracking_fetcher_
.reset(content::ResourceFetcher::Create(tracking_url
));
291 tracking_fetcher_
->SetMethod("POST");
292 tracking_fetcher_
->SetBody(tracking_request_body
);
293 tracking_fetcher_
->SetHeader("Content-Type", "application/json");
295 tracking_fetcher_
->Start(
297 blink::WebURLRequest::RequestContextInternal
,
298 blink::WebURLRequest::FrameTypeTopLevel
,
299 content::ResourceFetcher::PLATFORM_LOADER
,
300 base::Bind(&NetErrorHelper::OnTrackingRequestComplete
,
301 base::Unretained(this)));
304 void NetErrorHelper::ReloadPage() {
305 render_frame()->GetWebFrame()->reload(false);
308 void NetErrorHelper::LoadPageFromCache(const GURL
& page_url
) {
309 blink::WebFrame
* web_frame
= render_frame()->GetWebFrame();
310 DCHECK(!base::EqualsASCII(
311 base::StringPiece16(web_frame
->dataSource()->request().httpMethod()),
314 blink::WebURLRequest
request(page_url
);
315 request
.setCachePolicy(blink::WebURLRequest::ReturnCacheDataDontLoad
);
317 web_frame
->loadRequest(request
);
320 void NetErrorHelper::DiagnoseError(const GURL
& page_url
) {
321 render_frame()->Send(new ChromeViewHostMsg_RunNetworkDiagnostics(
322 render_frame()->GetRoutingID(), page_url
));
325 void NetErrorHelper::OnNetErrorInfo(int status_num
) {
326 DCHECK(status_num
>= 0 && status_num
< error_page::DNS_PROBE_MAX
);
328 DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num
);
330 core_
->OnNetErrorInfo(static_cast<DnsProbeStatus
>(status_num
));
333 void NetErrorHelper::OnSetCanShowNetworkDiagnosticsDialog(
334 bool can_use_local_diagnostics_service
) {
335 core_
->OnSetCanShowNetworkDiagnosticsDialog(
336 can_use_local_diagnostics_service
);
339 void NetErrorHelper::OnSetNavigationCorrectionInfo(
340 const GURL
& navigation_correction_url
,
341 const std::string
& language
,
342 const std::string
& country_code
,
343 const std::string
& api_key
,
344 const GURL
& search_url
) {
345 core_
->OnSetNavigationCorrectionInfo(navigation_correction_url
, language
,
346 country_code
, api_key
, search_url
);
349 void NetErrorHelper::OnNavigationCorrectionsFetched(
350 const blink::WebURLResponse
& response
,
351 const std::string
& data
) {
352 // The fetcher may only be deleted after |data| is passed to |core_|. Move
353 // it to a temporary to prevent any potential re-entrancy issues.
354 scoped_ptr
<content::ResourceFetcher
> fetcher(
355 correction_fetcher_
.release());
356 bool success
= (!response
.isNull() && response
.httpStatusCode() == 200);
357 core_
->OnNavigationCorrectionsFetched(
359 render_frame()->GetRenderView()->GetAcceptLanguages(),
360 base::i18n::IsRTL());
363 void NetErrorHelper::OnTrackingRequestComplete(
364 const blink::WebURLResponse
& response
,
365 const std::string
& data
) {
366 tracking_fetcher_
.reset();