Home | History | Annotate | Download | only in net
      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 
     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   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 
    271 void NetErrorHelper::CancelFetchNavigationCorrections() {
    272   correction_fetcher_.reset();
    273 }
    274 
    275 void 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 
    294 void NetErrorHelper::ReloadPage() {
    295   render_frame()->GetWebFrame()->reload(false);
    296 }
    297 
    298 void 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 
    308 void 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 
    316 void 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 
    326 void 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 
    340 void NetErrorHelper::OnTrackingRequestComplete(
    341     const blink::WebURLResponse& response,
    342     const std::string& data) {
    343   tracking_fetcher_.reset();
    344 }
    345