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/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/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" 41 #include "url/gurl.h" 42 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; 51 52 namespace { 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. 56 static const int kNavigationCorrectionFetchTimeoutSec = 3; 57 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; 63 } 64 65 NetErrorHelperCore::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 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, 83 auto_reload_enabled, 84 auto_reload_visible_only, 85 !render_frame->IsHidden())); 86 } 87 88 NetErrorHelper::~NetErrorHelper() { 89 RenderThread::Get()->RemoveObserver(this); 90 } 91 92 void NetErrorHelper::ReloadButtonPressed() { 93 core_->ExecuteButtonPress(NetErrorHelperCore::RELOAD_BUTTON); 94 } 95 96 void NetErrorHelper::LoadStaleButtonPressed() { 97 core_->ExecuteButtonPress(NetErrorHelperCore::LOAD_STALE_BUTTON); 98 } 99 100 void NetErrorHelper::MoreButtonPressed() { 101 core_->ExecuteButtonPress(NetErrorHelperCore::MORE_BUTTON); 102 } 103 104 void NetErrorHelper::DidStartProvisionalLoad() { 105 blink::WebFrame* frame = render_frame()->GetWebFrame(); 106 core_->OnStartLoad(GetFrameType(frame), GetLoadingPageType(frame)); 107 } 108 109 void NetErrorHelper::DidCommitProvisionalLoad(bool is_new_navigation) { 110 blink::WebFrame* frame = render_frame()->GetWebFrame(); 111 core_->OnCommitLoad(GetFrameType(frame), frame->document().url()); 112 } 113 114 void NetErrorHelper::DidFinishLoad() { 115 blink::WebFrame* frame = render_frame()->GetWebFrame(); 116 core_->OnFinishLoad(GetFrameType(frame)); 117 } 118 119 void NetErrorHelper::OnStop() { 120 core_->OnStop(); 121 } 122 123 void NetErrorHelper::WasShown() { 124 core_->OnWasShown(); 125 } 126 127 void NetErrorHelper::WasHidden() { 128 core_->OnWasHidden(); 129 } 130 131 bool 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 144 void NetErrorHelper::NetworkStateChanged(bool enabled) { 145 core_->NetworkStateChanged(enabled); 146 } 147 148 void 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 156 bool NetErrorHelper::ShouldSuppressErrorPage(blink::WebFrame* frame, 157 const GURL& url) { 158 return core_->ShouldSuppressErrorPage(GetFrameType(frame), url); 159 } 160 161 void NetErrorHelper::TrackClick(int tracking_id) { 162 core_->TrackClick(tracking_id); 163 } 164 165 void 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 201 void 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 210 void NetErrorHelper::EnablePageHelperFunctions() { 211 NetErrorPageController::Install(render_frame()); 212 } 213 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); 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 247 void 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 263 correction_fetcher_->Start( 264 frame, 265 blink::WebURLRequest::RequestContextInternal, 266 blink::WebURLRequest::FrameTypeTopLevel, 267 content::ResourceFetcher::PLATFORM_LOADER, 268 base::Bind(&NetErrorHelper::OnNavigationCorrectionsFetched, 269 base::Unretained(this))); 270 271 correction_fetcher_->SetTimeout( 272 base::TimeDelta::FromSeconds(kNavigationCorrectionFetchTimeoutSec)); 273 } 274 275 void NetErrorHelper::CancelFetchNavigationCorrections() { 276 correction_fetcher_.reset(); 277 } 278 279 void NetErrorHelper::SendTrackingRequest( 280 const GURL& tracking_url, 281 const std::string& tracking_request_body) { 282 blink::WebView* web_view = render_frame()->GetRenderView()->GetWebView(); 283 if (!web_view) 284 return; 285 blink::WebFrame* frame = web_view->mainFrame(); 286 287 // If there's already a pending tracking request, this will cancel it. 288 tracking_fetcher_.reset(content::ResourceFetcher::Create(tracking_url)); 289 tracking_fetcher_->SetMethod("POST"); 290 tracking_fetcher_->SetBody(tracking_request_body); 291 tracking_fetcher_->SetHeader("Content-Type", "application/json"); 292 293 tracking_fetcher_->Start( 294 frame, 295 blink::WebURLRequest::RequestContextInternal, 296 blink::WebURLRequest::FrameTypeTopLevel, 297 content::ResourceFetcher::PLATFORM_LOADER, 298 base::Bind(&NetErrorHelper::OnTrackingRequestComplete, 299 base::Unretained(this))); 300 } 301 302 void NetErrorHelper::ReloadPage() { 303 render_frame()->GetWebFrame()->reload(false); 304 } 305 306 void NetErrorHelper::LoadPageFromCache(const GURL& page_url) { 307 blink::WebFrame* web_frame = render_frame()->GetWebFrame(); 308 DCHECK(!EqualsASCII(web_frame->dataSource()->request().httpMethod(), "POST")); 309 310 blink::WebURLRequest request(page_url); 311 request.setCachePolicy(blink::WebURLRequest::ReturnCacheDataDontLoad); 312 313 web_frame->loadRequest(request); 314 } 315 316 void NetErrorHelper::OnNetErrorInfo(int status_num) { 317 DCHECK(status_num >= 0 && status_num < chrome_common_net::DNS_PROBE_MAX); 318 319 DVLOG(1) << "Received status " << DnsProbeStatusToString(status_num); 320 321 core_->OnNetErrorInfo(static_cast<DnsProbeStatus>(status_num)); 322 } 323 324 void NetErrorHelper::OnSetNavigationCorrectionInfo( 325 const GURL& navigation_correction_url, 326 const std::string& language, 327 const std::string& country_code, 328 const std::string& api_key, 329 const GURL& search_url) { 330 core_->OnSetNavigationCorrectionInfo(navigation_correction_url, language, 331 country_code, api_key, search_url); 332 } 333 334 void NetErrorHelper::OnNavigationCorrectionsFetched( 335 const blink::WebURLResponse& response, 336 const std::string& data) { 337 // The fetcher may only be deleted after |data| is passed to |core_|. Move 338 // it to a temporary to prevent any potential re-entrancy issues. 339 scoped_ptr<content::ResourceFetcher> fetcher( 340 correction_fetcher_.release()); 341 bool success = (!response.isNull() && response.httpStatusCode() == 200); 342 core_->OnNavigationCorrectionsFetched( 343 success ? data : "", 344 render_frame()->GetRenderView()->GetAcceptLanguages(), 345 base::i18n::IsRTL()); 346 } 347 348 void NetErrorHelper::OnTrackingRequestComplete( 349 const blink::WebURLResponse& response, 350 const std::string& data) { 351 tracking_fetcher_.reset(); 352 } 353