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/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/renderer/net/net_error_page_controller.h" 20#include "content/public/common/content_client.h" 21#include "content/public/common/url_constants.h" 22#include "content/public/renderer/content_renderer_client.h" 23#include "content/public/renderer/document_state.h" 24#include "content/public/renderer/render_frame.h" 25#include "content/public/renderer/render_thread.h" 26#include "content/public/renderer/render_view.h" 27#include "content/public/renderer/resource_fetcher.h" 28#include "grit/renderer_resources.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" 41#include "url/gurl.h" 42 43using base::JSONWriter; 44using chrome_common_net::DnsProbeStatus; 45using chrome_common_net::DnsProbeStatusToString; 46using content::DocumentState; 47using content::RenderFrame; 48using content::RenderFrameObserver; 49using content::RenderThread; 50using content::kUnreachableWebDataURL; 51 52namespace { 53 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. 56static const int kNavigationCorrectionFetchTimeoutSec = 3; 57 58NetErrorHelperCore::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; 63} 64 65NetErrorHelperCore::FrameType GetFrameType(const blink::WebFrame* frame) { 66 if (!frame->parent()) 67 return NetErrorHelperCore::MAIN_FRAME; 68 return NetErrorHelperCore::SUB_FRAME; 69} 70 71} // namespace 72 73NetErrorHelper::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, 83 auto_reload_enabled, 84 auto_reload_visible_only, 85 !render_frame->IsHidden())); 86} 87 88NetErrorHelper::~NetErrorHelper() { 89 RenderThread::Get()->RemoveObserver(this); 90} 91 92void NetErrorHelper::ReloadButtonPressed() { 93 core_->ExecuteButtonPress(NetErrorHelperCore::RELOAD_BUTTON); 94} 95 96void NetErrorHelper::LoadStaleButtonPressed() { 97 core_->ExecuteButtonPress(NetErrorHelperCore::LOAD_STALE_BUTTON); 98} 99 100void NetErrorHelper::MoreButtonPressed() { 101 core_->ExecuteButtonPress(NetErrorHelperCore::MORE_BUTTON); 102} 103 104void NetErrorHelper::DidStartProvisionalLoad() { 105 blink::WebFrame* frame = render_frame()->GetWebFrame(); 106 core_->OnStartLoad(GetFrameType(frame), GetLoadingPageType(frame)); 107} 108 109void NetErrorHelper::DidCommitProvisionalLoad(bool is_new_navigation) { 110 blink::WebFrame* frame = render_frame()->GetWebFrame(); 111 core_->OnCommitLoad(GetFrameType(frame), frame->document().url()); 112} 113 114void NetErrorHelper::DidFinishLoad() { 115 blink::WebFrame* frame = render_frame()->GetWebFrame(); 116 core_->OnFinishLoad(GetFrameType(frame)); 117} 118 119void NetErrorHelper::OnStop() { 120 core_->OnStop(); 121} 122 123void NetErrorHelper::WasShown() { 124 core_->OnWasShown(); 125} 126 127void NetErrorHelper::WasHidden() { 128 core_->OnWasHidden(); 129} 130 131bool NetErrorHelper::OnMessageReceived(const IPC::Message& message) { 132 bool handled = true; 133 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() 140 141 return handled; 142} 143 144void NetErrorHelper::NetworkStateChanged(bool enabled) { 145 core_->NetworkStateChanged(enabled); 146} 147 148void NetErrorHelper::GetErrorHTML( 149 blink::WebFrame* frame, 150 const blink::WebURLError& error, 151 bool is_failed_post, 152 std::string* error_html) { 153 core_->GetErrorHTML(GetFrameType(frame), error, is_failed_post, error_html); 154} 155 156bool NetErrorHelper::ShouldSuppressErrorPage(blink::WebFrame* frame, 157 const GURL& url) { 158 return core_->ShouldSuppressErrorPage(GetFrameType(frame), url); 159} 160 161void NetErrorHelper::TrackClick(int tracking_id) { 162 core_->TrackClick(tracking_id); 163} 164 165void NetErrorHelper::GenerateLocalizedErrorPage( 166 const blink::WebURLError& error, 167 bool is_failed_post, 168 scoped_ptr<LocalizedError::ErrorPageParams> params, 169 bool* reload_button_shown, 170 bool* load_stale_button_shown, 171 std::string* error_html) const { 172 error_html->clear(); 173 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."; 179 } else { 180 CommandLine* command_line = CommandLine::ForCurrentProcess(); 181 bool load_stale_cache_enabled = 182 command_line->HasSwitch(switches::kEnableOfflineLoadStaleCache); 183 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); 195 196 // "t" is the id of the template's root node. 197 *error_html = webui::GetTemplatesHtml(template_html, &error_strings, "t"); 198 } 199} 200 201void NetErrorHelper::LoadErrorPageInMainFrame(const std::string& html, 202 const GURL& failed_url) { 203 blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); 204 if (!web_view) 205 return; 206 blink::WebFrame* frame = web_view->mainFrame(); 207 frame->loadHTMLString(html, GURL(kUnreachableWebDataURL), failed_url, true); 208} 209 210void NetErrorHelper::EnablePageHelperFunctions() { 211 NetErrorPageController::Install(render_frame()); 212} 213 214void 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); 219 220 base::DictionaryValue error_strings; 221 LocalizedError::GetStrings(error.reason, 222 error.domain.utf8(), 223 error.unreachableURL, 224 is_failed_post, 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>(), 231 &error_strings); 232 233 std::string json; 234 JSONWriter::Write(&error_strings, &json); 235 236 std::string js = "if (window.updateForDnsProbe) " 237 "updateForDnsProbe(" + json + ");"; 238 base::string16 js16; 239 if (!base::UTF8ToUTF16(js.c_str(), js.length(), &js16)) { 240 NOTREACHED(); 241 return; 242 } 243 244 render_frame()->ExecuteJavaScript(js16); 245} 246 247void NetErrorHelper::FetchNavigationCorrections( 248 const GURL& navigation_correction_url, 249 const std::string& navigation_correction_request_body) { 250 DCHECK(!correction_fetcher_.get()); 251 252 blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); 253 if (!web_view) 254 return; 255 blink::WebFrame* frame = web_view->mainFrame(); 256 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"); 262 correction_fetcher_->Start( 263 frame, blink::WebURLRequest::TargetIsMainFrame, 264 base::Bind(&NetErrorHelper::OnNavigationCorrectionsFetched, 265 base::Unretained(this))); 266 267 correction_fetcher_->SetTimeout( 268 base::TimeDelta::FromSeconds(kNavigationCorrectionFetchTimeoutSec)); 269} 270 271void NetErrorHelper::CancelFetchNavigationCorrections() { 272 correction_fetcher_.reset(); 273} 274 275void NetErrorHelper::SendTrackingRequest( 276 const GURL& tracking_url, 277 const std::string& tracking_request_body) { 278 blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); 279 if (!web_view) 280 return; 281 blink::WebFrame* frame = web_view->mainFrame(); 282 283 // If there's already a pending tracking request, this will cancel it. 284 tracking_fetcher_.reset(content::ResourceFetcher::Create(tracking_url)); 285 tracking_fetcher_->SetMethod("POST"); 286 tracking_fetcher_->SetBody(tracking_request_body); 287 tracking_fetcher_->SetHeader("Content-Type", "application/json"); 288 tracking_fetcher_->Start( 289 frame, blink::WebURLRequest::TargetIsMainFrame, 290 base::Bind(&NetErrorHelper::OnTrackingRequestComplete, 291 base::Unretained(this))); 292} 293 294void NetErrorHelper::ReloadPage() { 295 render_frame()->GetWebFrame()->reload(false); 296} 297 298void NetErrorHelper::LoadPageFromCache(const GURL& page_url) { 299 blink::WebFrame* web_frame = render_frame()->GetWebFrame(); 300 DCHECK(!EqualsASCII(web_frame->dataSource()->request().httpMethod(), "POST")); 301 302 blink::WebURLRequest request(page_url); 303 request.setCachePolicy(blink::WebURLRequest::ReturnCacheDataDontLoad); 304 305 web_frame->loadRequest(request); 306} 307 308void NetErrorHelper::OnNetErrorInfo(int status_num) { 309 DCHECK(status_num >= 0 && status_num < chrome_common_net::DNS_PROBE_MAX); 310 311 DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num); 312 313 core_->OnNetErrorInfo(static_cast<DnsProbeStatus>(status_num)); 314} 315 316void NetErrorHelper::OnSetNavigationCorrectionInfo( 317 const GURL& navigation_correction_url, 318 const std::string& language, 319 const std::string& country_code, 320 const std::string& api_key, 321 const GURL& search_url) { 322 core_->OnSetNavigationCorrectionInfo(navigation_correction_url, language, 323 country_code, api_key, search_url); 324} 325 326void NetErrorHelper::OnNavigationCorrectionsFetched( 327 const blink::WebURLResponse& response, 328 const std::string& data) { 329 // The fetcher may only be deleted after |data| is passed to |core_|. Move 330 // it to a temporary to prevent any potential re-entrancy issues. 331 scoped_ptr<content::ResourceFetcher> fetcher( 332 correction_fetcher_.release()); 333 bool success = (!response.isNull() && response.httpStatusCode() == 200); 334 core_->OnNavigationCorrectionsFetched( 335 success ? data : "", 336 render_frame()->GetRenderView()->GetAcceptLanguages(), 337 base::i18n::IsRTL()); 338} 339 340void NetErrorHelper::OnTrackingRequestComplete( 341 const blink::WebURLResponse& response, 342 const std::string& data) { 343 tracking_fetcher_.reset(); 344} 345