Home | History | Annotate | Download | only in prerender
      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 <set>
      6 #include <utility>
      7 
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/run_loop.h"
     12 #include "base/threading/sequenced_worker_pool.h"
     13 #include "chrome/browser/net/url_request_mock_util.h"
     14 #include "chrome/browser/prerender/prerender_contents.h"
     15 #include "chrome/browser/prerender/prerender_manager.h"
     16 #include "chrome/browser/prerender/prerender_resource_throttle.h"
     17 #include "chrome/browser/prerender/prerender_tracker.h"
     18 #include "chrome/test/base/testing_browser_process.h"
     19 #include "content/public/browser/resource_controller.h"
     20 #include "content/public/browser/resource_request_info.h"
     21 #include "content/public/test/test_browser_thread.h"
     22 #include "ipc/ipc_message.h"
     23 #include "net/base/request_priority.h"
     24 #include "net/test/url_request/url_request_mock_http_job.h"
     25 #include "net/url_request/redirect_info.h"
     26 #include "net/url_request/url_request.h"
     27 #include "net/url_request/url_request_test_util.h"
     28 #include "testing/gtest/include/gtest/gtest.h"
     29 
     30 using content::BrowserThread;
     31 using content::ResourceType;
     32 
     33 namespace prerender {
     34 
     35 namespace {
     36 
     37 class TestPrerenderContents : public PrerenderContents {
     38  public:
     39   TestPrerenderContents(PrerenderManager* prerender_manager,
     40                         int child_id, int route_id)
     41       : PrerenderContents(prerender_manager, static_cast<Profile*>(NULL),
     42                           GURL(), content::Referrer(), ORIGIN_NONE,
     43                           PrerenderManager::kNoExperiment),
     44         child_id_(child_id),
     45         route_id_(route_id) {
     46     PrerenderResourceThrottle::OverridePrerenderContentsForTesting(this);
     47   }
     48 
     49   virtual ~TestPrerenderContents() {
     50     if (final_status() == FINAL_STATUS_MAX)
     51       SetFinalStatus(FINAL_STATUS_USED);
     52     PrerenderResourceThrottle::OverridePrerenderContentsForTesting(NULL);
     53   }
     54 
     55   virtual bool GetChildId(int* child_id) const OVERRIDE {
     56     *child_id = child_id_;
     57     return true;
     58   }
     59 
     60   virtual bool GetRouteId(int* route_id) const OVERRIDE {
     61     *route_id = route_id_;
     62     return true;
     63   }
     64 
     65   void Start() {
     66     prerendering_has_started_ = true;
     67     NotifyPrerenderStart();
     68   }
     69 
     70   void Cancel() {
     71     Destroy(FINAL_STATUS_CANCELLED);
     72   }
     73 
     74   void Use() {
     75     PrepareForUse();
     76   }
     77 
     78  private:
     79   int child_id_;
     80   int route_id_;
     81 };
     82 
     83 class TestPrerenderManager : public PrerenderManager {
     84  public:
     85   explicit TestPrerenderManager(PrerenderTracker* prerender_tracker) :
     86       PrerenderManager(NULL, prerender_tracker) {
     87     mutable_config().rate_limit_enabled = false;
     88   }
     89 
     90   // We never allocate our PrerenderContents in PrerenderManager, so we don't
     91   // ever want the default pending delete behaviour.
     92   virtual void MoveEntryToPendingDelete(PrerenderContents* entry,
     93                                         FinalStatus final_status) OVERRIDE {
     94   }
     95 };
     96 
     97 class DeferredRedirectDelegate : public net::URLRequest::Delegate,
     98                                  public content::ResourceController {
     99  public:
    100   DeferredRedirectDelegate()
    101       : throttle_(NULL),
    102         was_deferred_(false),
    103         cancel_called_(false),
    104         resume_called_(false) {
    105   }
    106 
    107   void SetThrottle(PrerenderResourceThrottle* throttle) {
    108     throttle_ = throttle;
    109     throttle_->set_controller_for_testing(this);
    110   }
    111 
    112   void Run() {
    113     run_loop_.reset(new base::RunLoop());
    114     run_loop_->Run();
    115   }
    116 
    117   bool was_deferred() const { return was_deferred_; }
    118   bool cancel_called() const { return cancel_called_; }
    119   bool resume_called() const { return resume_called_; }
    120 
    121   // net::URLRequest::Delegate implementation:
    122   virtual void OnReceivedRedirect(net::URLRequest* request,
    123                                   const net::RedirectInfo& redirect_info,
    124                                   bool* defer_redirect) OVERRIDE {
    125     // Defer the redirect either way.
    126     *defer_redirect = true;
    127 
    128     // Find out what the throttle would have done.
    129     throttle_->WillRedirectRequest(redirect_info.new_url, &was_deferred_);
    130     run_loop_->Quit();
    131   }
    132   virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { }
    133   virtual void OnReadCompleted(net::URLRequest* request,
    134                                int bytes_read) OVERRIDE {
    135   }
    136 
    137   // content::ResourceController implementation:
    138   virtual void Cancel() OVERRIDE {
    139     EXPECT_FALSE(cancel_called_);
    140     EXPECT_FALSE(resume_called_);
    141 
    142     cancel_called_ = true;
    143     run_loop_->Quit();
    144   }
    145   virtual void CancelAndIgnore() OVERRIDE { Cancel(); }
    146   virtual void CancelWithError(int error_code) OVERRIDE { Cancel(); }
    147   virtual void Resume() OVERRIDE {
    148     EXPECT_TRUE(was_deferred_);
    149     EXPECT_FALSE(cancel_called_);
    150     EXPECT_FALSE(resume_called_);
    151 
    152     resume_called_ = true;
    153     run_loop_->Quit();
    154   }
    155 
    156  private:
    157   scoped_ptr<base::RunLoop> run_loop_;
    158   PrerenderResourceThrottle* throttle_;
    159   bool was_deferred_;
    160   bool cancel_called_;
    161   bool resume_called_;
    162 
    163   DISALLOW_COPY_AND_ASSIGN(DeferredRedirectDelegate);
    164 };
    165 
    166 }  // namespace
    167 
    168 class PrerenderTrackerTest : public testing::Test {
    169  public:
    170   static const int kDefaultChildId = 0;
    171   static const int kDefaultRouteId = 100;
    172 
    173   PrerenderTrackerTest() :
    174       ui_thread_(BrowserThread::UI, &message_loop_),
    175       io_thread_(BrowserThread::IO, &message_loop_),
    176       prerender_manager_(prerender_tracker()),
    177       test_contents_(&prerender_manager_, kDefaultChildId, kDefaultRouteId) {
    178     chrome_browser_net::SetUrlRequestMocksEnabled(true);
    179   }
    180 
    181   virtual ~PrerenderTrackerTest() {
    182     chrome_browser_net::SetUrlRequestMocksEnabled(false);
    183 
    184     // Cleanup work so the file IO tasks from URLRequestMockHTTPJob
    185     // are gone.
    186     content::BrowserThread::GetBlockingPool()->FlushForTesting();
    187     RunEvents();
    188   }
    189 
    190   PrerenderTracker* prerender_tracker() {
    191     return g_browser_process->prerender_tracker();
    192   }
    193 
    194   TestPrerenderManager* prerender_manager() {
    195     return &prerender_manager_;
    196   }
    197 
    198   TestPrerenderContents* test_contents() {
    199     return &test_contents_;
    200   }
    201 
    202   // Runs any tasks queued on either thread.
    203   void RunEvents() {
    204     message_loop_.RunUntilIdle();
    205   }
    206 
    207  private:
    208   base::MessageLoopForIO message_loop_;
    209   content::TestBrowserThread ui_thread_;
    210   content::TestBrowserThread io_thread_;
    211 
    212   TestPrerenderManager prerender_manager_;
    213   TestPrerenderContents test_contents_;
    214 };
    215 
    216 // Checks that deferred redirects are throttled and resumed correctly.
    217 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectResume) {
    218   const base::FilePath::CharType kRedirectPath[] =
    219       FILE_PATH_LITERAL("prerender/image-deferred.png");
    220 
    221   test_contents()->Start();
    222   RunEvents();
    223 
    224   // Fake a request.
    225   net::TestURLRequestContext url_request_context;
    226   DeferredRedirectDelegate delegate;
    227   scoped_ptr<net::URLRequest> request(url_request_context.CreateRequest(
    228       net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)),
    229       net::DEFAULT_PRIORITY,
    230       &delegate,
    231       NULL));
    232   content::ResourceRequestInfo::AllocateForTesting(request.get(),
    233                                                    content::RESOURCE_TYPE_IMAGE,
    234                                                    NULL,
    235                                                    kDefaultChildId,
    236                                                    kDefaultRouteId,
    237                                                    MSG_ROUTING_NONE,
    238                                                    true);
    239 
    240   // Install a prerender throttle.
    241   PrerenderResourceThrottle throttle(request.get());
    242   delegate.SetThrottle(&throttle);
    243 
    244   // Start the request and wait for a redirect.
    245   request->Start();
    246   delegate.Run();
    247   EXPECT_TRUE(delegate.was_deferred());
    248   // This calls WillRedirectRequestOnUI().
    249   RunEvents();
    250 
    251   // Display the prerendered RenderView and wait for the throttle to
    252   // notice.
    253   test_contents()->Use();
    254   delegate.Run();
    255   EXPECT_TRUE(delegate.resume_called());
    256   EXPECT_FALSE(delegate.cancel_called());
    257 }
    258 
    259 // Checks that redirects in main frame loads are not deferred.
    260 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectMainFrame) {
    261   const base::FilePath::CharType kRedirectPath[] =
    262       FILE_PATH_LITERAL("prerender/image-deferred.png");
    263 
    264   test_contents()->Start();
    265   RunEvents();
    266 
    267   // Fake a request.
    268   net::TestURLRequestContext url_request_context;
    269   DeferredRedirectDelegate delegate;
    270   scoped_ptr<net::URLRequest> request(url_request_context.CreateRequest(
    271       net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)),
    272       net::DEFAULT_PRIORITY,
    273       &delegate,
    274       NULL));
    275   content::ResourceRequestInfo::AllocateForTesting(
    276       request.get(),
    277       content::RESOURCE_TYPE_MAIN_FRAME,
    278       NULL,
    279       kDefaultChildId,
    280       kDefaultRouteId,
    281       MSG_ROUTING_NONE,
    282       true);
    283 
    284   // Install a prerender throttle.
    285   PrerenderResourceThrottle throttle(request.get());
    286   delegate.SetThrottle(&throttle);
    287 
    288   // Start the request and wait for a redirect. This time, it should
    289   // not be deferred.
    290   request->Start();
    291   delegate.Run();
    292   // This calls WillRedirectRequestOnUI().
    293   RunEvents();
    294 
    295   // Cleanup work so the prerender is gone.
    296   test_contents()->Cancel();
    297   RunEvents();
    298 }
    299 
    300 // Checks that attempting to defer a synchronous request aborts the
    301 // prerender.
    302 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectSyncXHR) {
    303   const base::FilePath::CharType kRedirectPath[] =
    304       FILE_PATH_LITERAL("prerender/image-deferred.png");
    305 
    306   test_contents()->Start();
    307   RunEvents();
    308 
    309   // Fake a request.
    310   net::TestURLRequestContext url_request_context;
    311   DeferredRedirectDelegate delegate;
    312   scoped_ptr<net::URLRequest> request(url_request_context.CreateRequest(
    313       net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)),
    314       net::DEFAULT_PRIORITY,
    315       &delegate,
    316       NULL));
    317   content::ResourceRequestInfo::AllocateForTesting(request.get(),
    318                                                    content::RESOURCE_TYPE_XHR,
    319                                                    NULL,
    320                                                    kDefaultChildId,
    321                                                    kDefaultRouteId,
    322                                                    MSG_ROUTING_NONE,
    323                                                    false);
    324 
    325   // Install a prerender throttle.
    326   PrerenderResourceThrottle throttle(request.get());
    327   delegate.SetThrottle(&throttle);
    328 
    329   // Start the request and wait for a redirect.
    330   request->Start();
    331   delegate.Run();
    332   // This calls WillRedirectRequestOnUI().
    333   RunEvents();
    334 
    335   // We should have cancelled the prerender.
    336   EXPECT_EQ(FINAL_STATUS_BAD_DEFERRED_REDIRECT,
    337             test_contents()->final_status());
    338 
    339   // Cleanup work so the prerender is gone.
    340   test_contents()->Cancel();
    341   RunEvents();
    342 }
    343 
    344 }  // namespace prerender
    345