// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/renderer/net/net_error_helper.h" #include #include "base/command_line.h" #include "base/i18n/rtl.h" #include "base/json/json_writer.h" #include "base/metrics/histogram.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/localized_error.h" #include "chrome/common/render_messages.h" #include "chrome/grit/renderer_resources.h" #include "chrome/renderer/net/net_error_page_controller.h" #include "components/error_page/common/error_page_params.h" #include "components/error_page/common/net_error_info.h" #include "content/public/common/content_client.h" #include "content/public/common/url_constants.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/resource_fetcher.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" #include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebView.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/webui/jstemplate_builder.h" #include "url/gurl.h" using base::JSONWriter; using chrome_common_net::DnsProbeStatus; using chrome_common_net::DnsProbeStatusToString; using content::DocumentState; using content::RenderFrame; using content::RenderFrameObserver; using content::RenderThread; using content::kUnreachableWebDataURL; using error_page::ErrorPageParams; using error_page::NetErrorHelperCore; namespace { // Number of seconds to wait for the navigation correction service to return // suggestions. If it takes too long, just use the local error page. static const int kNavigationCorrectionFetchTimeoutSec = 3; NetErrorHelperCore::PageType GetLoadingPageType(const blink::WebFrame* frame) { GURL url = frame->provisionalDataSource()->request().url(); if (!url.is_valid() || url.spec() != kUnreachableWebDataURL) return NetErrorHelperCore::NON_ERROR_PAGE; return NetErrorHelperCore::ERROR_PAGE; } NetErrorHelperCore::FrameType GetFrameType(const blink::WebFrame* frame) { if (!frame->parent()) return NetErrorHelperCore::MAIN_FRAME; return NetErrorHelperCore::SUB_FRAME; } } // namespace NetErrorHelper::NetErrorHelper(RenderFrame* render_frame) : RenderFrameObserver(render_frame), content::RenderFrameObserverTracker(render_frame) { RenderThread::Get()->AddObserver(this); CommandLine* command_line = CommandLine::ForCurrentProcess(); bool auto_reload_enabled = command_line->HasSwitch(switches::kEnableOfflineAutoReload); bool auto_reload_visible_only = command_line->HasSwitch(switches::kEnableOfflineAutoReloadVisibleOnly); core_.reset(new NetErrorHelperCore(this, auto_reload_enabled, auto_reload_visible_only, !render_frame->IsHidden())); } NetErrorHelper::~NetErrorHelper() { RenderThread::Get()->RemoveObserver(this); } void NetErrorHelper::ReloadButtonPressed() { core_->ExecuteButtonPress(NetErrorHelperCore::RELOAD_BUTTON); } void NetErrorHelper::LoadStaleButtonPressed() { core_->ExecuteButtonPress(NetErrorHelperCore::LOAD_STALE_BUTTON); } void NetErrorHelper::MoreButtonPressed() { core_->ExecuteButtonPress(NetErrorHelperCore::MORE_BUTTON); } void NetErrorHelper::DidStartProvisionalLoad() { blink::WebFrame* frame = render_frame()->GetWebFrame(); core_->OnStartLoad(GetFrameType(frame), GetLoadingPageType(frame)); } void NetErrorHelper::DidCommitProvisionalLoad(bool is_new_navigation) { blink::WebFrame* frame = render_frame()->GetWebFrame(); core_->OnCommitLoad(GetFrameType(frame), frame->document().url()); } void NetErrorHelper::DidFinishLoad() { blink::WebFrame* frame = render_frame()->GetWebFrame(); core_->OnFinishLoad(GetFrameType(frame)); } void NetErrorHelper::OnStop() { core_->OnStop(); } void NetErrorHelper::WasShown() { core_->OnWasShown(); } void NetErrorHelper::WasHidden() { core_->OnWasHidden(); } bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(NetErrorHelper, message) IPC_MESSAGE_HANDLER(ChromeViewMsg_NetErrorInfo, OnNetErrorInfo) IPC_MESSAGE_HANDLER(ChromeViewMsg_SetNavigationCorrectionInfo, OnSetNavigationCorrectionInfo); IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void NetErrorHelper::NetworkStateChanged(bool enabled) { core_->NetworkStateChanged(enabled); } void NetErrorHelper::GetErrorHTML( blink::WebFrame* frame, const blink::WebURLError& error, bool is_failed_post, std::string* error_html) { core_->GetErrorHTML(GetFrameType(frame), error, is_failed_post, error_html); } bool NetErrorHelper::ShouldSuppressErrorPage(blink::WebFrame* frame, const GURL& url) { return core_->ShouldSuppressErrorPage(GetFrameType(frame), url); } void NetErrorHelper::TrackClick(int tracking_id) { core_->TrackClick(tracking_id); } void NetErrorHelper::GenerateLocalizedErrorPage( const blink::WebURLError& error, bool is_failed_post, scoped_ptr params, bool* reload_button_shown, bool* load_stale_button_shown, std::string* error_html) const { error_html->clear(); int resource_id = IDR_NET_ERROR_HTML; const base::StringPiece template_html( ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id)); if (template_html.empty()) { NOTREACHED() << "unable to load template."; } else { CommandLine* command_line = CommandLine::ForCurrentProcess(); bool load_stale_cache_enabled = command_line->HasSwitch(switches::kEnableOfflineLoadStaleCache); base::DictionaryValue error_strings; LocalizedError::GetStrings(error.reason, error.domain.utf8(), error.unreachableURL, is_failed_post, (load_stale_cache_enabled && error.staleCopyInCache && !is_failed_post), RenderThread::Get()->GetLocale(), render_frame()->GetRenderView()-> GetAcceptLanguages(), params.Pass(), &error_strings); *reload_button_shown = error_strings.Get("reloadButton", NULL); *load_stale_button_shown = error_strings.Get("staleLoadButton", NULL); // "t" is the id of the template's root node. *error_html = webui::GetTemplatesHtml(template_html, &error_strings, "t"); } } void NetErrorHelper::LoadErrorPageInMainFrame(const std::string& html, const GURL& failed_url) { blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); if (!web_view) return; blink::WebFrame* frame = web_view->mainFrame(); frame->loadHTMLString(html, GURL(kUnreachableWebDataURL), failed_url, true); } void NetErrorHelper::EnablePageHelperFunctions() { NetErrorPageController::Install(render_frame()); } void NetErrorHelper::UpdateErrorPage(const blink::WebURLError& error, bool is_failed_post) { CommandLine* command_line = CommandLine::ForCurrentProcess(); bool load_stale_cache_enabled = command_line->HasSwitch(switches::kEnableOfflineLoadStaleCache); base::DictionaryValue error_strings; LocalizedError::GetStrings(error.reason, error.domain.utf8(), error.unreachableURL, is_failed_post, (load_stale_cache_enabled && error.staleCopyInCache && !is_failed_post), RenderThread::Get()->GetLocale(), render_frame()->GetRenderView()-> GetAcceptLanguages(), scoped_ptr(), &error_strings); std::string json; JSONWriter::Write(&error_strings, &json); std::string js = "if (window.updateForDnsProbe) " "updateForDnsProbe(" + json + ");"; base::string16 js16; if (!base::UTF8ToUTF16(js.c_str(), js.length(), &js16)) { NOTREACHED(); return; } render_frame()->ExecuteJavaScript(js16); } void NetErrorHelper::FetchNavigationCorrections( const GURL& navigation_correction_url, const std::string& navigation_correction_request_body) { DCHECK(!correction_fetcher_.get()); blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); if (!web_view) return; blink::WebFrame* frame = web_view->mainFrame(); correction_fetcher_.reset( content::ResourceFetcher::Create(navigation_correction_url)); correction_fetcher_->SetMethod("POST"); correction_fetcher_->SetBody(navigation_correction_request_body); correction_fetcher_->SetHeader("Content-Type", "application/json"); correction_fetcher_->Start( frame, blink::WebURLRequest::RequestContextInternal, blink::WebURLRequest::FrameTypeTopLevel, content::ResourceFetcher::PLATFORM_LOADER, base::Bind(&NetErrorHelper::OnNavigationCorrectionsFetched, base::Unretained(this))); correction_fetcher_->SetTimeout( base::TimeDelta::FromSeconds(kNavigationCorrectionFetchTimeoutSec)); } void NetErrorHelper::CancelFetchNavigationCorrections() { correction_fetcher_.reset(); } void NetErrorHelper::SendTrackingRequest( const GURL& tracking_url, const std::string& tracking_request_body) { blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); if (!web_view) return; blink::WebFrame* frame = web_view->mainFrame(); // If there's already a pending tracking request, this will cancel it. tracking_fetcher_.reset(content::ResourceFetcher::Create(tracking_url)); tracking_fetcher_->SetMethod("POST"); tracking_fetcher_->SetBody(tracking_request_body); tracking_fetcher_->SetHeader("Content-Type", "application/json"); tracking_fetcher_->Start( frame, blink::WebURLRequest::RequestContextInternal, blink::WebURLRequest::FrameTypeTopLevel, content::ResourceFetcher::PLATFORM_LOADER, base::Bind(&NetErrorHelper::OnTrackingRequestComplete, base::Unretained(this))); } void NetErrorHelper::ReloadPage() { render_frame()->GetWebFrame()->reload(false); } void NetErrorHelper::LoadPageFromCache(const GURL& page_url) { blink::WebFrame* web_frame = render_frame()->GetWebFrame(); DCHECK(!EqualsASCII(web_frame->dataSource()->request().httpMethod(), "POST")); blink::WebURLRequest request(page_url); request.setCachePolicy(blink::WebURLRequest::ReturnCacheDataDontLoad); web_frame->loadRequest(request); } void NetErrorHelper::OnNetErrorInfo(int status_num) { DCHECK(status_num >= 0 && status_num < chrome_common_net::DNS_PROBE_MAX); DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num); core_->OnNetErrorInfo(static_cast(status_num)); } void NetErrorHelper::OnSetNavigationCorrectionInfo( const GURL& navigation_correction_url, const std::string& language, const std::string& country_code, const std::string& api_key, const GURL& search_url) { core_->OnSetNavigationCorrectionInfo(navigation_correction_url, language, country_code, api_key, search_url); } void NetErrorHelper::OnNavigationCorrectionsFetched( const blink::WebURLResponse& response, const std::string& data) { // The fetcher may only be deleted after |data| is passed to |core_|. Move // it to a temporary to prevent any potential re-entrancy issues. scoped_ptr fetcher( correction_fetcher_.release()); bool success = (!response.isNull() && response.httpStatusCode() == 200); core_->OnNavigationCorrectionsFetched( success ? data : "", render_frame()->GetRenderView()->GetAcceptLanguages(), base::i18n::IsRTL()); } void NetErrorHelper::OnTrackingRequestComplete( const blink::WebURLResponse& response, const std::string& data) { tracking_fetcher_.reset(); }