Home | History | Annotate | Download | only in renderer
      1 // Copyright (c) 2012 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 "content/renderer/fetchers/resource_fetcher.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/command_line.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/timer/timer.h"
     12 #include "content/public/common/content_switches.h"
     13 #include "content/public/common/url_constants.h"
     14 #include "content/public/renderer/render_view.h"
     15 #include "content/public/test/test_utils.h"
     16 #include "content/shell/shell.h"
     17 #include "content/test/content_browser_test.h"
     18 #include "content/test/content_browser_test_utils.h"
     19 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     20 #include "third_party/WebKit/public/web/WebFrame.h"
     21 #include "third_party/WebKit/public/web/WebView.h"
     22 
     23 using WebKit::WebFrame;
     24 using WebKit::WebURLRequest;
     25 using WebKit::WebURLResponse;
     26 
     27 namespace content {
     28 
     29 static const int kMaxWaitTimeMs = 5000;
     30 
     31 class FetcherDelegate {
     32  public:
     33   FetcherDelegate()
     34       : completed_(false),
     35         timed_out_(false) {
     36     // Start a repeating timer waiting for the download to complete.  The
     37     // callback has to be a static function, so we hold on to our instance.
     38     FetcherDelegate::instance_ = this;
     39     StartTimer();
     40   }
     41 
     42   virtual ~FetcherDelegate() {}
     43 
     44   ResourceFetcher::Callback NewCallback() {
     45     return base::Bind(&FetcherDelegate::OnURLFetchComplete,
     46                       base::Unretained(this));
     47   }
     48 
     49   virtual void OnURLFetchComplete(const WebURLResponse& response,
     50                                   const std::string& data) {
     51     response_ = response;
     52     data_ = data;
     53     completed_ = true;
     54     timer_.Stop();
     55     if (!timed_out_)
     56       quit_task_.Run();
     57   }
     58 
     59   bool completed() const { return completed_; }
     60   bool timed_out() const { return timed_out_; }
     61 
     62   std::string data() const { return data_; }
     63   const WebURLResponse& response() const { return response_; }
     64 
     65   // Wait for the request to complete or timeout.
     66   void WaitForResponse() {
     67     scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner;
     68     quit_task_ = runner->QuitClosure();
     69     runner->Run();
     70   }
     71 
     72   void StartTimer() {
     73     timer_.Start(FROM_HERE,
     74                  base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs),
     75                  this,
     76                  &FetcherDelegate::TimerFired);
     77   }
     78 
     79   void TimerFired() {
     80     ASSERT_FALSE(completed_);
     81 
     82     timed_out_ = true;
     83     if (!completed_)
     84       quit_task_.Run();
     85     FAIL() << "fetch timed out";
     86   }
     87 
     88   static FetcherDelegate* instance_;
     89 
     90  private:
     91   base::OneShotTimer<FetcherDelegate> timer_;
     92   bool completed_;
     93   bool timed_out_;
     94   WebURLResponse response_;
     95   std::string data_;
     96   base::Closure quit_task_;
     97 };
     98 
     99 FetcherDelegate* FetcherDelegate::instance_ = NULL;
    100 
    101 class EvilFetcherDelegate : public FetcherDelegate {
    102  public:
    103   virtual ~EvilFetcherDelegate() {}
    104 
    105   void SetFetcher(ResourceFetcher* fetcher) {
    106     fetcher_.reset(fetcher);
    107   }
    108 
    109   virtual void OnURLFetchComplete(const WebURLResponse& response,
    110                                   const std::string& data) OVERRIDE {
    111     // Destroy the ResourceFetcher here.  We are testing that upon returning
    112     // to the ResourceFetcher that it does not crash.
    113     fetcher_.reset();
    114     FetcherDelegate::OnURLFetchComplete(response, data);
    115   }
    116 
    117  private:
    118   scoped_ptr<ResourceFetcher> fetcher_;
    119 };
    120 
    121 class ResourceFetcherTests : public ContentBrowserTest {
    122  public:
    123   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    124     command_line->AppendSwitch(switches::kSingleProcess);
    125 #if defined(OS_WIN) && defined(USE_AURA)
    126     // Don't want to try to create a GPU process.
    127     command_line->AppendSwitch(switches::kDisableAcceleratedCompositing);
    128 #endif
    129   }
    130 
    131   RenderView* GetRenderView() {
    132     // We could have the test on the UI thread get the WebContent's routing ID,
    133     // but we know this will be the first RV so skip that and just hardcode it.
    134     return RenderView::FromRoutingID(1);
    135   }
    136 
    137   void ResourceFetcherDownloadOnRenderer(const GURL& url) {
    138     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    139 
    140     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    141     scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
    142         url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
    143 
    144     delegate->WaitForResponse();
    145 
    146     ASSERT_TRUE(delegate->completed());
    147     EXPECT_EQ(delegate->response().httpStatusCode(), 200);
    148     std::string text = delegate->data();
    149     EXPECT_TRUE(text.find("Basic html test.") != std::string::npos);
    150   }
    151 
    152   void ResourceFetcher404OnRenderer(const GURL& url) {
    153     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    154 
    155     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    156     scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
    157         url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
    158 
    159     delegate->WaitForResponse();
    160 
    161     ASSERT_TRUE(delegate->completed());
    162     EXPECT_EQ(delegate->response().httpStatusCode(), 404);
    163     EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
    164   }
    165 
    166   void ResourceFetcherDidFailOnRenderer() {
    167     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    168 
    169     // Try to fetch a page on a site that doesn't exist.
    170     GURL url("http://localhost:1339/doesnotexist");
    171     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    172     scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
    173         url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
    174 
    175     delegate->WaitForResponse();
    176 
    177     // When we fail, we still call the Delegate callback but we pass in empty
    178     // values.
    179     EXPECT_TRUE(delegate->completed());
    180     EXPECT_TRUE(delegate->response().isNull());
    181     EXPECT_EQ(delegate->data(), std::string());
    182     EXPECT_FALSE(delegate->timed_out());
    183   }
    184 
    185   void ResourceFetcherTimeoutOnRenderer(const GURL& url) {
    186     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    187 
    188     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    189     scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
    190         url, frame, WebURLRequest::TargetIsMainFrame,
    191         0, delegate->NewCallback()));
    192 
    193     delegate->WaitForResponse();
    194 
    195     // When we timeout, we still call the Delegate callback but we pass in empty
    196     // values.
    197     EXPECT_TRUE(delegate->completed());
    198     EXPECT_TRUE(delegate->response().isNull());
    199     EXPECT_EQ(delegate->data(), std::string());
    200     EXPECT_FALSE(delegate->timed_out());
    201   }
    202 
    203   void ResourceFetcherDeletedInCallbackOnRenderer(const GURL& url) {
    204     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    205 
    206     scoped_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate);
    207     scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
    208         url, frame, WebURLRequest::TargetIsMainFrame,
    209         0, delegate->NewCallback()));
    210     delegate->SetFetcher(fetcher.release());
    211 
    212     delegate->WaitForResponse();
    213     EXPECT_FALSE(delegate->timed_out());
    214   }
    215 };
    216 
    217 // Test a fetch from the test server.
    218 // If this flakes, use http://crbug.com/51622.
    219 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDownload) {
    220   // Need to spin up the renderer.
    221   NavigateToURL(shell(), GURL(kAboutBlankURL));
    222 
    223   ASSERT_TRUE(test_server()->Start());
    224   GURL url(test_server()->GetURL("files/simple_page.html"));
    225 
    226   PostTaskToInProcessRendererAndWait(
    227         base::Bind(&ResourceFetcherTests::ResourceFetcherDownloadOnRenderer,
    228                    base::Unretained(this), url));
    229 }
    230 
    231 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcher404) {
    232   // Need to spin up the renderer.
    233   NavigateToURL(shell(), GURL(kAboutBlankURL));
    234 
    235   // Test 404 response.
    236   ASSERT_TRUE(test_server()->Start());
    237   GURL url = test_server()->GetURL("files/thisfiledoesntexist.html");
    238 
    239   PostTaskToInProcessRendererAndWait(
    240         base::Bind(&ResourceFetcherTests::ResourceFetcher404OnRenderer,
    241                    base::Unretained(this), url));
    242 }
    243 
    244 // If this flakes, use http://crbug.com/51622.
    245 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) {
    246   // Need to spin up the renderer.
    247   NavigateToURL(shell(), GURL(kAboutBlankURL));
    248 
    249   PostTaskToInProcessRendererAndWait(
    250         base::Bind(&ResourceFetcherTests::ResourceFetcherDidFailOnRenderer,
    251                    base::Unretained(this)));
    252 }
    253 
    254 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
    255   // Need to spin up the renderer.
    256   NavigateToURL(shell(), GURL(kAboutBlankURL));
    257 
    258   // Grab a page that takes at least 1 sec to respond, but set the fetcher to
    259   // timeout in 0 sec.
    260   ASSERT_TRUE(test_server()->Start());
    261   GURL url(test_server()->GetURL("slow?1"));
    262 
    263   PostTaskToInProcessRendererAndWait(
    264         base::Bind(&ResourceFetcherTests::ResourceFetcherTimeoutOnRenderer,
    265                    base::Unretained(this), url));
    266 }
    267 
    268 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) {
    269   // Need to spin up the renderer.
    270   NavigateToURL(shell(), GURL(kAboutBlankURL));
    271 
    272   // Grab a page that takes at least 1 sec to respond, but set the fetcher to
    273   // timeout in 0 sec.
    274   ASSERT_TRUE(test_server()->Start());
    275   GURL url(test_server()->GetURL("slow?1"));
    276 
    277   PostTaskToInProcessRendererAndWait(
    278         base::Bind(
    279             &ResourceFetcherTests::ResourceFetcherDeletedInCallbackOnRenderer,
    280             base::Unretained(this), url));
    281 }
    282 
    283 }  // namespace content
    284